synaptipy 0.1.1b4__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 (102) hide show
  1. Synaptipy/__init__.py +21 -0
  2. Synaptipy/__main__.py +86 -0
  3. Synaptipy/application/__init__.py +11 -0
  4. Synaptipy/application/__main__.py +251 -0
  5. Synaptipy/application/cli/__init__.py +11 -0
  6. Synaptipy/application/cli/main.py +10 -0
  7. Synaptipy/application/controllers/__init__.py +19 -0
  8. Synaptipy/application/controllers/analysis_formatter.py +539 -0
  9. Synaptipy/application/controllers/analysis_plot_manager.py +181 -0
  10. Synaptipy/application/controllers/file_io_controller.py +188 -0
  11. Synaptipy/application/controllers/live_analysis_controller.py +145 -0
  12. Synaptipy/application/controllers/shortcut_manager.py +57 -0
  13. Synaptipy/application/data_loader.py +160 -0
  14. Synaptipy/application/gui/__init__.py +12 -0
  15. Synaptipy/application/gui/about_dialog.py +123 -0
  16. Synaptipy/application/gui/analyser_tab.py +961 -0
  17. Synaptipy/application/gui/analysis_config_dialog.py +196 -0
  18. Synaptipy/application/gui/analysis_tabs/__init__.py +10 -0
  19. Synaptipy/application/gui/analysis_tabs/base.py +2704 -0
  20. Synaptipy/application/gui/analysis_tabs/metadata_driven.py +1789 -0
  21. Synaptipy/application/gui/analysis_worker.py +215 -0
  22. Synaptipy/application/gui/batch_dialog.py +1184 -0
  23. Synaptipy/application/gui/dialogs/export_manager.py +224 -0
  24. Synaptipy/application/gui/dialogs/plot_export_dialog.py +68 -0
  25. Synaptipy/application/gui/dialogs/trial_selection_dialog.py +93 -0
  26. Synaptipy/application/gui/explorer/__init__.py +6 -0
  27. Synaptipy/application/gui/explorer/config_panel.py +327 -0
  28. Synaptipy/application/gui/explorer/explorer_tab.py +2217 -0
  29. Synaptipy/application/gui/explorer/plot_canvas.py +231 -0
  30. Synaptipy/application/gui/explorer/sidebar.py +414 -0
  31. Synaptipy/application/gui/explorer/toolbar.py +131 -0
  32. Synaptipy/application/gui/explorer/y_controls.py +258 -0
  33. Synaptipy/application/gui/exporter_tab.py +606 -0
  34. Synaptipy/application/gui/main_window.py +1096 -0
  35. Synaptipy/application/gui/nwb_dialog.py +341 -0
  36. Synaptipy/application/gui/plot_customization_dialog.py +862 -0
  37. Synaptipy/application/gui/plot_save_dialog.py +336 -0
  38. Synaptipy/application/gui/preferences_dialog.py +374 -0
  39. Synaptipy/application/gui/session_summary_dialog.py +97 -0
  40. Synaptipy/application/gui/ui_generator.py +230 -0
  41. Synaptipy/application/gui/welcome_screen.py +482 -0
  42. Synaptipy/application/gui/widgets/plot_canvas.py +620 -0
  43. Synaptipy/application/gui/widgets/preprocessing.py +329 -0
  44. Synaptipy/application/plugin_manager.py +276 -0
  45. Synaptipy/application/services/__init__.py +3 -0
  46. Synaptipy/application/services/data_loader_service.py +108 -0
  47. Synaptipy/application/session_manager.py +235 -0
  48. Synaptipy/application/startup_manager.py +280 -0
  49. Synaptipy/core/__init__.py +19 -0
  50. Synaptipy/core/analysis/__init__.py +42 -0
  51. Synaptipy/core/analysis/batch_engine.py +1127 -0
  52. Synaptipy/core/analysis/cross_file_utils.py +151 -0
  53. Synaptipy/core/analysis/epoch_manager.py +291 -0
  54. Synaptipy/core/analysis/evoked_responses.py +910 -0
  55. Synaptipy/core/analysis/firing_dynamics.py +679 -0
  56. Synaptipy/core/analysis/passive_properties.py +2185 -0
  57. Synaptipy/core/analysis/registry.py +244 -0
  58. Synaptipy/core/analysis/single_spike.py +925 -0
  59. Synaptipy/core/analysis/synaptic_events.py +1397 -0
  60. Synaptipy/core/data_model.py +548 -0
  61. Synaptipy/core/processing_pipeline.py +345 -0
  62. Synaptipy/core/results.py +147 -0
  63. Synaptipy/core/signal_processor.py +839 -0
  64. Synaptipy/core/source_interfaces.py +28 -0
  65. Synaptipy/infrastructure/__init__.py +12 -0
  66. Synaptipy/infrastructure/exporters/__init__.py +15 -0
  67. Synaptipy/infrastructure/exporters/csv_exporter.py +618 -0
  68. Synaptipy/infrastructure/exporters/nwb_exporter.py +454 -0
  69. Synaptipy/infrastructure/file_readers/__init__.py +16 -0
  70. Synaptipy/infrastructure/file_readers/abf_reader.py +1 -0
  71. Synaptipy/infrastructure/file_readers/neo_adapter.py +702 -0
  72. Synaptipy/infrastructure/file_readers/neo_source_handle.py +83 -0
  73. Synaptipy/infrastructure/neo_patches.py +281 -0
  74. Synaptipy/resources/icons/logo.afdesign +0 -0
  75. Synaptipy/resources/icons/logo.icns +0 -0
  76. Synaptipy/resources/icons/logo.ico +0 -0
  77. Synaptipy/resources/icons/logo.png +0 -0
  78. Synaptipy/shared/__init__.py +103 -0
  79. Synaptipy/shared/constants.py +49 -0
  80. Synaptipy/shared/data_cache.py +279 -0
  81. Synaptipy/shared/error_handling.py +70 -0
  82. Synaptipy/shared/logging_config.py +135 -0
  83. Synaptipy/shared/plot_customization.py +924 -0
  84. Synaptipy/shared/plot_exporter.py +245 -0
  85. Synaptipy/shared/plot_factory.py +297 -0
  86. Synaptipy/shared/plot_zoom_sync.py +550 -0
  87. Synaptipy/shared/scroll_settings.py +131 -0
  88. Synaptipy/shared/styling.py +326 -0
  89. Synaptipy/shared/theme_manager.py +708 -0
  90. Synaptipy/shared/utils.py +46 -0
  91. Synaptipy/shared/viewbox.py +116 -0
  92. Synaptipy/shared/zoom_theme.py +350 -0
  93. Synaptipy/templates/analysis_template.py +62 -0
  94. Synaptipy/templates/plugin_template.py +168 -0
  95. Synaptipy/templates/tab_template.py +89 -0
  96. Synaptipy/templates/test_template.py +45 -0
  97. synaptipy-0.1.1b4.dist-info/METADATA +635 -0
  98. synaptipy-0.1.1b4.dist-info/RECORD +102 -0
  99. synaptipy-0.1.1b4.dist-info/WHEEL +5 -0
  100. synaptipy-0.1.1b4.dist-info/entry_points.txt +2 -0
  101. synaptipy-0.1.1b4.dist-info/licenses/LICENSE +657 -0
  102. synaptipy-0.1.1b4.dist-info/top_level.txt +1 -0
Synaptipy/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Synaptipy: A multi-channel electrophysiology visualization and analysis toolkit.
4
+
5
+ This package provides tools for loading, visualizing, and exporting
6
+ electrophysiology data using the neo library and a Qt-based graphical interface.
7
+ """
8
+
9
+ # PEP 396 style version marker
10
+ __version__ = "0.1.1b4"
11
+ __author__ = "Anzal K Shahul"
12
+ __email__ = "anzal.ks@gmail.com"
13
+ __license__ = "AGPL-3.0-or-later"
14
+
15
+ # Define the primary public API exposed directly by 'import Synaptipy'
16
+ # Usually just metadata for library packages.
17
+ __all__ = ["__version__", "__author__", "__email__", "__license__"]
18
+
19
+ # Optional: Configure root logger basic settings if not done elsewhere
20
+ # import logging
21
+ # logging.getLogger(__name__).addHandler(logging.NullHandler())
Synaptipy/__main__.py ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Synaptipy - Multi-channel Electrophysiology Visualization and Analysis Toolkit
4
+
5
+ This module serves as the entry point for the package when run as:
6
+ python -m Synaptipy
7
+
8
+ It parses command line arguments and launches the application with the appropriate settings.
9
+
10
+ This file is part of Synaptipy, licensed under the GNU Affero General Public License v3.0.
11
+ See the LICENSE file in the root of the repository for full license details.
12
+ """
13
+
14
+ import argparse
15
+ import logging
16
+ import os
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ # Set up logging before importing the rest of the package
21
+ from Synaptipy.shared.logging_config import setup_logging
22
+
23
+
24
+ def parse_args():
25
+ """Parse command line arguments."""
26
+ parser = argparse.ArgumentParser(
27
+ description="Synaptipy - Electrophysiology Visualization and Analysis Toolkit",
28
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
29
+ )
30
+ parser.add_argument("--dev", action="store_true", help="Run in development mode with increased logging")
31
+ parser.add_argument("--log-dir", type=str, default=None, help="Directory to store log files")
32
+ parser.add_argument("--verbose", "-v", action="store_true", help="Increase output verbosity")
33
+ parser.add_argument("--version", action="store_true", help="Show version information and exit")
34
+ parser.add_argument("--file", type=str, default=None, help="Open a specific file on startup")
35
+
36
+ return parser.parse_args()
37
+
38
+
39
+ def main():
40
+ """Main entry point for the application."""
41
+ args = parse_args()
42
+
43
+ # Show version and exit if requested
44
+ if args.version:
45
+ from Synaptipy import __version__
46
+
47
+ print(f"Synaptipy version {__version__}")
48
+ return 0
49
+
50
+ # Set up environment variables based on arguments
51
+ if args.dev:
52
+ os.environ["SYNAPTIPY_DEV_MODE"] = "1"
53
+
54
+ # Configure logging
55
+ _log_level = logging.DEBUG if args.verbose or args.dev else logging.INFO # noqa: F841
56
+ setup_logging(dev_mode=args.dev, log_dir=args.log_dir)
57
+ logger = logging.getLogger(__name__)
58
+
59
+ logger.info("Starting Synaptipy...")
60
+ logger.debug(f"Command line arguments: {args}")
61
+
62
+ # Import GUI components here to avoid circular imports
63
+ try:
64
+ from Synaptipy.application.__main__ import run_gui
65
+
66
+ # Launch the GUI
67
+ initial_file = Path(args.file) if args.file else None
68
+ if initial_file and not initial_file.exists():
69
+ logger.error(f"File not found: {initial_file}")
70
+ print(f"Error: File not found: {initial_file}")
71
+ return 1
72
+
73
+ return run_gui()
74
+ except ImportError as e:
75
+ logger.error(f"Failed to import GUI components: {e}")
76
+ print(f"Error: Failed to import GUI components: {e}")
77
+ print("Make sure PySide6 is installed properly.")
78
+ return 1
79
+ except Exception as e:
80
+ logger.exception(f"Unexpected error: {e}")
81
+ print(f"Error: {e}")
82
+ return 1
83
+
84
+
85
+ if __name__ == "__main__":
86
+ sys.exit(main())
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Application Layer for Synaptipy.
4
+
5
+ Contains the user interface (GUI, CLI) and logic that orchestrates
6
+ user interactions with the core domain and infrastructure layers.
7
+ """
8
+
9
+ # Nothing essential needs to be exposed directly from here for typical use.
10
+ # Users will typically interact via the main entry point or specific UI/CLI modules.
11
+ __all__ = []
@@ -0,0 +1,251 @@
1
+ # src/Synaptipy/application/__main__.py
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Main entry point for the Synaptipy Viewer GUI application.
5
+
6
+ This module is responsible for:
7
+ 1. Processing command line arguments
8
+ 2. Setting up the logging system with dev mode support
9
+ 3. Initializing the Qt application and UI styling
10
+ 4. Creating and displaying the welcome screen with startup manager
11
+ 5. Running the event loop
12
+
13
+ The module defines the `run_gui` function called by the entry point script
14
+ defined in pyproject.toml.
15
+
16
+ This file is part of Synaptipy, licensed under the GNU Affero General Public License v3.0.
17
+ See the LICENSE file in the root of the repository for full license details.
18
+ """
19
+
20
+ import argparse
21
+ import faulthandler
22
+ import logging
23
+ import os
24
+ import sys
25
+ import traceback
26
+
27
+ # Suppress annoying pyqtgraph RuntimeWarnings (overflow in cast)
28
+ import warnings
29
+
30
+ from PySide6 import QtCore, QtGui, QtWidgets
31
+
32
+ # --- Import Core Components ---
33
+ from Synaptipy.application.startup_manager import StartupManager
34
+ from Synaptipy.shared.logging_config import setup_logging
35
+
36
+ warnings.filterwarnings("ignore", category=RuntimeWarning, module="pyqtgraph")
37
+
38
+ # Enable low-level C traceback on SIGBUS/SIGSEGV so fatal crashes include a
39
+ # Python stack trace in the log (zero overhead in normal operation).
40
+ faulthandler.enable()
41
+
42
+ # Log instance to be initialized after setting up logging
43
+ log = logging.getLogger(__name__)
44
+
45
+
46
+ def parse_arguments():
47
+ """
48
+ Parse command line arguments for the application.
49
+
50
+ Returns:
51
+ argparse.Namespace: Parsed command line arguments
52
+ """
53
+ parser = argparse.ArgumentParser(description="Synaptipy - Electrophysiology Visualization Suite")
54
+ parser.add_argument("--dev", action="store_true", help="Enable development mode with verbose logging")
55
+ parser.add_argument("--log-dir", type=str, help="Custom directory for log files")
56
+ parser.add_argument("--log-file", type=str, help="Custom log filename")
57
+ return parser.parse_args()
58
+
59
+
60
+ # ---------------------------------------------------------------------------
61
+ # Graceful crash reporter
62
+ # ---------------------------------------------------------------------------
63
+
64
+
65
+ class CrashReportDialog(QtWidgets.QDialog):
66
+ """Non-modal dialog shown when an unhandled Python exception escapes to the
67
+ top level. Displays the full traceback in a read-only text area and
68
+ provides a one-click "Copy to Clipboard" button so the user can paste the
69
+ report directly into a GitHub Issue.
70
+ """
71
+
72
+ _GITHUB_ISSUES_URL = "https://github.com/anzalks/synaptipy/issues/new"
73
+
74
+ def __init__(self, traceback_text: str, parent=None):
75
+ super().__init__(parent)
76
+ self.setWindowTitle("Synaptipy — Unexpected Error")
77
+ self.setMinimumSize(680, 420)
78
+ self._traceback_text = traceback_text
79
+ self._build_ui()
80
+
81
+ def _build_ui(self) -> None:
82
+ layout = QtWidgets.QVBoxLayout(self)
83
+
84
+ # ---- Header ----
85
+ header = QtWidgets.QLabel(
86
+ "<b>An unexpected error occurred.</b><br>"
87
+ "The error details are shown below. "
88
+ "Please click <i>Copy to Clipboard</i> and paste them into a "
89
+ "<a href='{url}'>GitHub Issue</a> so we can fix this.".format(url=self._GITHUB_ISSUES_URL)
90
+ )
91
+ header.setWordWrap(True)
92
+ header.setOpenExternalLinks(True)
93
+ layout.addWidget(header)
94
+
95
+ # ---- Traceback ----
96
+ self._text_area = QtWidgets.QPlainTextEdit()
97
+ self._text_area.setReadOnly(True)
98
+ self._text_area.setPlainText(self._traceback_text)
99
+ font = QtGui.QFont("Courier New", 9)
100
+ font.setStyleHint(QtGui.QFont.StyleHint.Monospace)
101
+ self._text_area.setFont(font)
102
+ layout.addWidget(self._text_area)
103
+
104
+ # ---- Buttons ----
105
+ btn_box = QtWidgets.QDialogButtonBox()
106
+ copy_btn = btn_box.addButton("Copy to Clipboard", QtWidgets.QDialogButtonBox.ButtonRole.ActionRole)
107
+ close_btn = btn_box.addButton(QtWidgets.QDialogButtonBox.StandardButton.Close)
108
+ copy_btn.clicked.connect(self._copy_to_clipboard)
109
+ close_btn.clicked.connect(self.accept)
110
+ layout.addWidget(btn_box)
111
+
112
+ def _copy_to_clipboard(self) -> None:
113
+ QtWidgets.QApplication.clipboard().setText(self._traceback_text)
114
+
115
+
116
+ def _install_excepthook() -> None:
117
+ """Replace ``sys.excepthook`` with a GUI-aware crash reporter.
118
+
119
+ When an unhandled exception propagates to the top of the event loop,
120
+ the default hook prints to stderr and silently exits. Our replacement
121
+ logs the traceback, then pops up a ``CrashReportDialog`` so the user
122
+ can copy the error into a GitHub Issue.
123
+
124
+ The hook is intentionally *not* installed for ``SystemExit`` and
125
+ ``KeyboardInterrupt`` so normal shutdown and Ctrl-C still work.
126
+ """
127
+
128
+ def _excepthook(exc_type, exc_value, exc_tb):
129
+ if issubclass(exc_type, (SystemExit, KeyboardInterrupt)):
130
+ sys.__excepthook__(exc_type, exc_value, exc_tb)
131
+ return
132
+
133
+ tb_str = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
134
+ log.critical("Unhandled exception:\n%s", tb_str)
135
+
136
+ app = QtWidgets.QApplication.instance()
137
+ if app is not None:
138
+ try:
139
+ dlg = CrashReportDialog(tb_str)
140
+ dlg.exec()
141
+ except Exception:
142
+ # If the dialog itself fails, fall back to stderr
143
+ sys.__excepthook__(exc_type, exc_value, exc_tb)
144
+ else:
145
+ sys.__excepthook__(exc_type, exc_value, exc_tb)
146
+
147
+ sys.excepthook = _excepthook
148
+
149
+
150
+ def run_gui(): # noqa: C901
151
+ """
152
+ Set up and run the Synaptipy GUI application with welcome screen.
153
+
154
+ This function:
155
+ 1. Parses command line arguments
156
+ 2. Configures the logging system
157
+ 3. Initializes the Qt application
158
+ 4. Creates and displays the welcome screen
159
+ 5. Manages startup process with progress tracking
160
+ 6. Runs the Qt event loop
161
+
162
+ Returns:
163
+ int: The application exit code
164
+ """
165
+ # Parse command line arguments
166
+ args = parse_arguments()
167
+
168
+ # Check for dev mode environment variable
169
+ env_dev_mode = os.environ.get("SYNAPTIPY_DEV_MODE")
170
+ if env_dev_mode and env_dev_mode.lower() in ("1", "true", "yes"):
171
+ dev_mode = True
172
+ else:
173
+ dev_mode = args.dev
174
+
175
+ # Setup logging with the appropriate mode
176
+ setup_logging(dev_mode=dev_mode, log_dir=args.log_dir, log_filename=args.log_file)
177
+
178
+ log.info("Application starting...")
179
+ if dev_mode:
180
+ log.info("Running in DEVELOPMENT mode with verbose logging")
181
+
182
+ # Create Qt Application with High DPI support
183
+ app = QtWidgets.QApplication.instance()
184
+ if app is None:
185
+ # Enable High DPI scaling before creating QApplication
186
+ # Check if HighDpiScaleFactorRoundingPolicy is available (Qt 6.0+)
187
+ if hasattr(QtCore.Qt, "HighDpiScaleFactorRoundingPolicy"):
188
+ try:
189
+ QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy(
190
+ QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
191
+ )
192
+ log.debug("High DPI PassThrough policy set successfully")
193
+ except Exception as e:
194
+ log.warning(f"Could not set High DPI policy: {e}")
195
+
196
+ app = QtWidgets.QApplication(sys.argv)
197
+
198
+ # Enable High DPI pixmaps (for better icon rendering)
199
+ try:
200
+ app.setAttribute(QtCore.Qt.ApplicationAttribute.AA_UseHighDpiPixmaps, True)
201
+ except Exception as e:
202
+ log.warning(f"Could not enable High DPI pixmaps: {e}")
203
+
204
+ # Force locale to English/US to ensure dot decimal separators
205
+ # This fixes issues where European locales force comma separators in spinboxes
206
+ QtCore.QLocale.setDefault(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
207
+ log.debug("Forced application locale to English/US (dot decimal separator)")
208
+
209
+ # Install the GUI-aware crash reporter so unhandled exceptions show a
210
+ # dialog with a "Copy to Clipboard" button instead of silently exiting.
211
+ _install_excepthook()
212
+ log.debug("Crash-report excepthook installed.")
213
+
214
+ # Create startup manager and begin loading process
215
+ try:
216
+ startup_manager = StartupManager(app)
217
+ welcome_screen = startup_manager.start_loading()
218
+
219
+ # Show welcome screen immediately and force display
220
+ welcome_screen.show()
221
+ welcome_screen.raise_() # Bring to front
222
+ welcome_screen.activateWindow() # Activate the window
223
+
224
+ # Force Qt to process events and display the window immediately
225
+ app.processEvents()
226
+
227
+ # Use the optimized display method
228
+ welcome_screen.force_display()
229
+
230
+ log.debug("Welcome screen displayed, beginning startup process")
231
+
232
+ except Exception as e:
233
+ log.critical(f"Failed to create startup manager: {e}", exc_info=True)
234
+ try:
235
+ QtWidgets.QMessageBox.critical(
236
+ None, "Application Startup Error", f"Failed to create startup manager:\n{e}\n\nSee logs."
237
+ )
238
+ except Exception:
239
+ pass
240
+ sys.exit(1)
241
+
242
+ # Start Qt Event Loop
243
+ log.debug("Starting Qt event loop...")
244
+ exit_code = app.exec()
245
+ log.debug(f"Qt event loop finished with exit code {exit_code}.")
246
+ return exit_code
247
+
248
+
249
+ # Allow direct execution for development and testing
250
+ if __name__ == "__main__":
251
+ run_gui()
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Command-Line Interface (CLI) subpackage for Synaptipy.
4
+
5
+ This module is reserved for future CLI functionality.
6
+ The primary interface is the GUI, launched via ``synaptipy`` or
7
+ ``python -m Synaptipy.application``.
8
+ """
9
+
10
+ __all__: list = []
11
+ __all__ = []
@@ -0,0 +1,10 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Command-Line Interface (CLI) entry point for Synaptipy.
4
+
5
+ Reserved for future implementation. The primary interface is currently
6
+ the GUI application.
7
+ """
8
+
9
+ # Future CLI implementation using argparse or click.
10
+ # See docs/developer_guide.md for planned CLI features.
@@ -0,0 +1,19 @@
1
+ """Application controllers for Synaptipy."""
2
+
3
+ from .analysis_formatter import AnalysisResultFormatter, generate_methods_text
4
+ from .analysis_plot_manager import AnalysisPlotManager, PlotContextTrace, PlotDataPackage
5
+ from .file_io_controller import FileIOController
6
+ from .live_analysis_controller import AnalysisRunnable, LiveAnalysisController
7
+ from .shortcut_manager import ShortcutManager
8
+
9
+ __all__ = [
10
+ "AnalysisResultFormatter",
11
+ "generate_methods_text",
12
+ "AnalysisPlotManager",
13
+ "PlotContextTrace",
14
+ "PlotDataPackage",
15
+ "FileIOController",
16
+ "AnalysisRunnable",
17
+ "LiveAnalysisController",
18
+ "ShortcutManager",
19
+ ]