diff options
Diffstat (limited to 'ui/main_window.py')
| -rw-r--r-- | ui/main_window.py | 96 |
1 files changed, 93 insertions, 3 deletions
diff --git a/ui/main_window.py b/ui/main_window.py index 93fbef2..01e0505 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -108,10 +108,11 @@ from ui.profile_manager_ui import ProfileButton from ui.windows.config_window import ConfigWindow from ui.windows.plot_window import build_default_layout from ui.windows.settings_window import SettingsWindow +from ui.windows.debug_window import DebugWindow from plugins.plugin_manager import PluginManager from plugins.base_plugin import PluginContext -from core.app_settings import load_settings, save_settings +from core.app_settings import load_settings, save_settings, set_developer_mode _DARK_QSS = os.path.join(os.path.dirname(os.path.abspath(__file__)), "style_dark.qss") @@ -132,6 +133,7 @@ class MainWindow(QMainWindow): self._elapsed = 0 self._win_config = None self._win_settings = None + self._win_debug = None _plugins_dir = os.path.join(os.path.dirname(os.path.dirname( os.path.abspath(__file__))), "plugins") @@ -252,6 +254,12 @@ class MainWindow(QMainWindow): set_btn.clicked.connect(lambda c: self._open_settings()) tb.addWidget(set_btn); self._btn_settings = set_btn + debug_btn = QPushButton("π Debug") + debug_btn.setObjectName("toolbarSectionBtn"); debug_btn.setCheckable(True) + debug_btn.clicked.connect(lambda c: self._open_debug()) + tb.addWidget(debug_btn); self._btn_debug = debug_btn + debug_btn.setVisible(False) # shown/hidden by _update_debug_btn_visibility per developer-mode setting + # ββ Central βββββββββββββββββββββββββββββββββββββββββββββββββββββββ central = QWidget(); self.setCentralWidget(central) @@ -294,6 +302,9 @@ class MainWindow(QMainWindow): self._clock = QTimer(self); self._clock.setInterval(1000) self._clock.timeout.connect(self._tick) + self._rec_blink = QTimer(self); self._rec_blink.setInterval(600) + self._rec_blink.timeout.connect(self._tick_rec_blink) + def _on_ctrl_dock_visibility(self, visible: bool): self._ctrl_tab.setVisible(not visible) if hasattr(self, "_act_ctrl_panel"): @@ -409,11 +420,60 @@ class MainWindow(QMainWindow): def plugin_enable(self, plugin_id: str): """Called by SettingsWindow when user enables a plugin.""" + missing = self._plugin_mgr.get_missing_dependencies(plugin_id) + if missing: + from PyQt6.QtWidgets import QMessageBox + reply = QMessageBox.question( + self, "Missing Plugin Dependencies", + f"This plugin needs packages that aren't installed:\n\n" + f" {', '.join(missing)}\n\n" + f"Install them now with pip?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.No, + ) + if reply == QMessageBox.StandardButton.Yes: + if self._pip_install(missing): + self._enable_plugin_now(plugin_id) + else: + self._enable_plugin_now(plugin_id) + if self._win_settings: + self._win_settings.sync_plugin_button(plugin_id) + + def _enable_plugin_now(self, plugin_id: str): ctx = self._make_plugin_context() plugin = self._plugin_mgr.enable(plugin_id, ctx) if plugin: self._install_plugin(plugin) + def _pip_install(self, requirements: list) -> bool: + """Blocking `pip install` of the given requirement strings. + Returns True on success; shows a result dialog either way.""" + import subprocess + import sys + from PyQt6.QtWidgets import QMessageBox + + QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor) + try: + result = subprocess.run( + [sys.executable, "-m", "pip", "install", *requirements], + capture_output=True, text=True, + ) + finally: + QApplication.restoreOverrideCursor() + + if result.returncode == 0: + QMessageBox.information( + self, "Install Complete", + f"Installed: {', '.join(requirements)}" + ) + return True + QMessageBox.critical( + self, "Install Failed", + f"pip install failed for: {', '.join(requirements)}\n\n" + f"{result.stderr.strip()[-1500:]}" + ) + return False + def plugin_disable(self, plugin_id: str): """Called by SettingsWindow when user disables a plugin.""" self._uninstall_plugin(plugin_id) @@ -440,6 +500,12 @@ class MainWindow(QMainWindow): self._win_config.tabs.setCurrentIndex(tab) self._show_win(self._win_config, "right") + def _open_debug(self): + if self._win_debug is None: + self._win_debug = DebugWindow(self) + self._win_debug.closed.connect(lambda: self._btn_debug.setChecked(False)) + self._show_win(self._win_debug, "right") + def _on_config_closed(self): self._act_devices.setChecked(False) self._act_channels.setChecked(False) @@ -603,9 +669,12 @@ class MainWindow(QMainWindow): self._run_btn.setText("βΉ STOP"); self._log_btn.setEnabled(True) self._clock.start(); self._status.setText("Acquiringβ¦") else: + self._ctrl.safe_stop_all() # master switch β stop outputs before halting acquisition self.engine.stop() self._run_btn.setText("βΆ RUN") - if self._log_btn.isChecked(): self._log_btn.setChecked(False) + if self._log_btn.isChecked(): + self._log_btn.setChecked(False) + self._toggle_log(False) # setChecked() alone won't fire clicked β stop blink/logging explicitly self._log_btn.setEnabled(False); self._clock.stop() self._status.setText("Stopped") @@ -621,8 +690,18 @@ class MainWindow(QMainWindow): os.path.join(self._settings.get("log_dir", "logs"), "")) self._log_btn.setText("βΉ LOGGING") self._status.setText(f"Logging β {p}") + self._rec_blink.start() else: self.engine.stop_logging(); self._log_btn.setText("⬀ LOG") + self._rec_blink.stop() + self._log_btn.setProperty("recording", False) + self._log_btn.style().unpolish(self._log_btn); self._log_btn.style().polish(self._log_btn) + + def _tick_rec_blink(self): + """Pulse the Log button's background while a recording is active.""" + on = not self._log_btn.property("recording") + self._log_btn.setProperty("recording", on) + self._log_btn.style().unpolish(self._log_btn); self._log_btn.style().polish(self._log_btn) # ββ Theme / settings ββββββββββββββββββββββββββββββββββββββββββββββββββ @@ -641,6 +720,17 @@ class MainWindow(QMainWindow): def _apply_developer_mode(self, enabled: bool): level = logging.DEBUG if enabled else logging.WARNING logging.getLogger().setLevel(level) + set_developer_mode(enabled) + self._update_debug_btn_visibility() + + def _update_debug_btn_visibility(self): + from core.app_settings import is_developer_mode + on = is_developer_mode() + self._btn_debug.setVisible(on) + if not on: + self._btn_debug.setChecked(False) + if self._win_debug: + self._win_debug.hide() def _on_settings(self, cfg: dict): self._settings.update(cfg) @@ -654,7 +744,7 @@ class MainWindow(QMainWindow): self._time_lbl.setText(f"{h:02d}:{m:02d}:{s:02d}") def closeEvent(self, event): - for w in (self._win_config, self._win_settings): + for w in (self._win_config, self._win_settings, self._win_debug): if w: w.close() for plugin in list(self._plugin_mgr.get_loaded()): try: |
