magpy-gui 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. magpy/__init__.py +16 -0
  2. magpy/__main__.py +5 -0
  3. magpy/app.py +49 -0
  4. magpy/cli.py +140 -0
  5. magpy/controllers/__init__.py +8 -0
  6. magpy/controllers/audio_controller.py +834 -0
  7. magpy/core/__init__.py +17 -0
  8. magpy/core/audio.py +312 -0
  9. magpy/core/measurements.py +581 -0
  10. magpy/core/selection.py +500 -0
  11. magpy/core/spectrogram.py +341 -0
  12. magpy/main_window.py +1231 -0
  13. magpy/screens/__init__.py +15 -0
  14. magpy/screens/base.py +67 -0
  15. magpy/screens/batch_screen.py +104 -0
  16. magpy/screens/pipeline_screen.py +503 -0
  17. magpy/screens/terminal.py +395 -0
  18. magpy/screens/training_screen.py +105 -0
  19. magpy/widgets/__init__.py +32 -0
  20. magpy/widgets/detection_dialog.py +614 -0
  21. magpy/widgets/detection_results.py +431 -0
  22. magpy/widgets/navigation_bar.py +117 -0
  23. magpy/widgets/node_graph/__init__.py +44 -0
  24. magpy/widgets/node_graph/connection.py +212 -0
  25. magpy/widgets/node_graph/graph_widget.py +434 -0
  26. magpy/widgets/node_graph/node.py +376 -0
  27. magpy/widgets/node_graph/node_palette.py +628 -0
  28. magpy/widgets/node_graph/parameter_panel.py +552 -0
  29. magpy/widgets/playback.py +381 -0
  30. magpy/widgets/properties_panel.py +295 -0
  31. magpy/widgets/selection_table.py +944 -0
  32. magpy/widgets/spectrogram.py +787 -0
  33. magpy/widgets/waveform.py +364 -0
  34. magpy/workers/__init__.py +19 -0
  35. magpy/workers/detection_worker.py +586 -0
  36. magpy_gui-0.1.4.dist-info/METADATA +44 -0
  37. magpy_gui-0.1.4.dist-info/RECORD +40 -0
  38. magpy_gui-0.1.4.dist-info/WHEEL +4 -0
  39. magpy_gui-0.1.4.dist-info/entry_points.txt +5 -0
  40. magpy_gui-0.1.4.dist-info/licenses/LICENSE +674 -0
magpy/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ """
2
+ MagPy - A modern Python bioacoustics analysis application.
3
+
4
+ MagPy provides tools for viewing, analyzing, and annotating sound files
5
+ with a focus on bioacoustics research.
6
+
7
+ Package Structure:
8
+ - core: Core analysis modules (audio, spectrogram, measurements, selection)
9
+ - widgets: PyQt6 widgets (waveform, spectrogram, annotation_table, playback)
10
+ - screens: Multi-screen workspace views
11
+ - controllers: Bridge between GUI and bioamla core
12
+ - workers: QThread workers for long-running operations
13
+ """
14
+
15
+ __version__ = "0.1.2"
16
+ __author__ = "John McMeen"
magpy/__main__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Entry point for running magpy as a module: python -m magpy"""
2
+ from magpy.app import main
3
+
4
+ if __name__ == "__main__":
5
+ main()
magpy/app.py ADDED
@@ -0,0 +1,49 @@
1
+ """
2
+ Main application entry point for MagPy GUI.
3
+ """
4
+
5
+ import sys
6
+ from typing import Optional
7
+
8
+ from PyQt6.QtWidgets import QApplication
9
+ from PyQt6.QtCore import Qt
10
+
11
+ from magpy.main_window import MainWindow
12
+
13
+
14
+ def main(filepath: Optional[str] = None) -> int:
15
+ """
16
+ Launch the MagPy application.
17
+
18
+ Args:
19
+ filepath: Optional audio file to open on startup.
20
+
21
+ Returns:
22
+ Application exit code.
23
+ """
24
+ # Enable high DPI support
25
+ QApplication.setHighDpiScaleFactorRoundingPolicy(
26
+ Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
27
+ )
28
+
29
+ app = QApplication(sys.argv)
30
+ app.setApplicationName("MagPy")
31
+ app.setOrganizationName("MagPy")
32
+ app.setOrganizationDomain("bioamla.org")
33
+
34
+ # Create main window
35
+ window = MainWindow()
36
+
37
+ # Open file if provided
38
+ if filepath:
39
+ window.load_file(filepath)
40
+ elif len(sys.argv) > 1:
41
+ window.load_file(sys.argv[1])
42
+
43
+ window.show()
44
+
45
+ return app.exec()
46
+
47
+
48
+ if __name__ == "__main__":
49
+ sys.exit(main())
magpy/cli.py ADDED
@@ -0,0 +1,140 @@
1
+ """
2
+ Command-line interface for MagPy.
3
+ """
4
+
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from . import __version__
10
+
11
+
12
+ def main() -> int:
13
+ """Main CLI entry point."""
14
+ parser = argparse.ArgumentParser(
15
+ prog="magpy",
16
+ description="MagPy Bioacoustics",
17
+ )
18
+
19
+ parser.add_argument(
20
+ "--version",
21
+ action="version",
22
+ version=f"%(prog)s {__version__}",
23
+ )
24
+
25
+ parser.add_argument(
26
+ "file",
27
+ nargs="?",
28
+ help="Audio file to open",
29
+ )
30
+
31
+ parser.add_argument(
32
+ "--gui",
33
+ action="store_true",
34
+ default=True,
35
+ help="Launch GUI (default)",
36
+ )
37
+
38
+ parser.add_argument(
39
+ "--headless",
40
+ action="store_true",
41
+ help="Run in headless mode for batch processing",
42
+ )
43
+
44
+ parser.add_argument(
45
+ "--compute-spectrogram",
46
+ metavar="OUTPUT",
47
+ help="Compute spectrogram and save as image",
48
+ )
49
+
50
+ parser.add_argument(
51
+ "--measure",
52
+ metavar="SELECTION_TABLE",
53
+ help="Compute measurements for selections in the given table",
54
+ )
55
+
56
+ args = parser.parse_args()
57
+
58
+ if args.headless:
59
+ return run_headless(args)
60
+ else:
61
+ from .app import main as gui_main
62
+
63
+ return gui_main(args.file)
64
+
65
+
66
+ def run_headless(args) -> int:
67
+ """Run in headless mode for batch processing."""
68
+ if args.file is None:
69
+ print("Error: No input file specified", file=sys.stderr)
70
+ return 1
71
+
72
+ filepath = Path(args.file)
73
+ if not filepath.exists():
74
+ print(f"Error: File not found: {filepath}", file=sys.stderr)
75
+ return 1
76
+
77
+ from .core import AudioFile, SpectrogramGenerator, SelectionTable, MeasurementCalculator
78
+ from .core.measurements import SelectionBounds
79
+
80
+ print(f"Loading: {filepath}")
81
+ audio = AudioFile(filepath)
82
+ print(f" Duration: {audio.duration:.2f}s")
83
+ print(f" Channels: {audio.num_channels}")
84
+ print(f" Sample rate: {audio.sample_rate} Hz")
85
+
86
+ if args.compute_spectrogram:
87
+ print(f"Computing spectrogram...")
88
+ generator = SpectrogramGenerator()
89
+ result = generator.compute(audio.data, audio.sample_rate)
90
+
91
+ import matplotlib.pyplot as plt
92
+
93
+ plt.figure(figsize=(12, 6))
94
+ plt.imshow(
95
+ result.spectrogram,
96
+ aspect="auto",
97
+ origin="lower",
98
+ extent=[0, result.duration, 0, result.frequencies[-1]],
99
+ cmap="viridis",
100
+ )
101
+ plt.colorbar(label="Power (dB)")
102
+ plt.xlabel("Time (s)")
103
+ plt.ylabel("Frequency (Hz)")
104
+ plt.title(f"Spectrogram: {filepath.name}")
105
+ plt.savefig(args.compute_spectrogram, dpi=150, bbox_inches="tight")
106
+ print(f"Saved spectrogram to: {args.compute_spectrogram}")
107
+
108
+ if args.measure:
109
+ selection_path = Path(args.measure)
110
+ if not selection_path.exists():
111
+ print(f"Error: Selection table not found: {selection_path}", file=sys.stderr)
112
+ return 1
113
+
114
+ print(f"Loading selection table: {selection_path}")
115
+ table = SelectionTable.load(selection_path)
116
+ print(f" Found {len(table)} selections")
117
+
118
+ calc = MeasurementCalculator()
119
+
120
+ for i, selection in enumerate(table):
121
+ bounds = SelectionBounds(
122
+ start_time=selection.begin_time,
123
+ end_time=selection.end_time,
124
+ low_freq=selection.low_freq,
125
+ high_freq=selection.high_freq,
126
+ )
127
+ result = calc.compute_all(audio.data, audio.sample_rate, bounds)
128
+ selection.measurements = result.to_dict()
129
+ print(f" Measured selection {i + 1}: {selection.duration:.3f}s")
130
+
131
+ # Save updated table
132
+ output_path = selection_path.with_stem(selection_path.stem + "_measured")
133
+ table.save(output_path)
134
+ print(f"Saved measurements to: {output_path}")
135
+
136
+ return 0
137
+
138
+
139
+ if __name__ == "__main__":
140
+ sys.exit(main())
@@ -0,0 +1,8 @@
1
+ """MagPy controllers - bridge between GUI and bioamla core."""
2
+
3
+ from .audio_controller import AudioController, AudioMetadata
4
+
5
+ __all__ = [
6
+ "AudioController",
7
+ "AudioMetadata",
8
+ ]