summaryrefslogtreecommitdiff
path: root/ui/profile_manager_ui.py
diff options
context:
space:
mode:
Diffstat (limited to 'ui/profile_manager_ui.py')
-rw-r--r--ui/profile_manager_ui.py152
1 files changed, 152 insertions, 0 deletions
diff --git a/ui/profile_manager_ui.py b/ui/profile_manager_ui.py
new file mode 100644
index 0000000..8966f8f
--- /dev/null
+++ b/ui/profile_manager_ui.py
@@ -0,0 +1,152 @@
+"""
+ui/profile_manager_ui.py
+
+Profile menu button (πŸ“) in the top-right toolbar.
+Handles New / Load / Save / Save As via file dialogs.
+Thin UI layer β€” all serialisation is in core/profile.py.
+"""
+
+import os
+from PyQt6.QtWidgets import (
+ QMenu, QPushButton, QFileDialog, QInputDialog,
+ QMessageBox, QApplication,
+)
+from PyQt6.QtCore import QPoint
+from PyQt6.QtGui import QAction
+
+from core.profile import Profile, ProfileManager
+
+
+PROFILE_EXT = ".labdaq"
+PROFILE_FILTER = f"LabDAQ Profile (*{PROFILE_EXT});;All files (*)"
+
+
+class ProfileButton(QPushButton):
+ """
+ A 'πŸ“ File' button that shows New / Load / Save / Save As.
+ Placed in the toolbar top-right.
+
+ Caller provides callbacks:
+ on_new() β€” reset to blank state
+ get_profile() β€” return a Profile representing current state
+ apply_profile(p) β€” restore state from a Profile
+ """
+
+ def __init__(self, on_new, get_profile, apply_profile, parent=None):
+ super().__init__("πŸ“ File", parent)
+ self.setObjectName("toolbarSectionBtn")
+ self._on_new = on_new
+ self._get_profile = get_profile
+ self._apply_profile = apply_profile
+ self._current_path: str = ""
+ self.clicked.connect(self._show_menu)
+
+ # ── Menu ──────────────────────────────────────────────────────────────────
+
+ def _show_menu(self):
+ menu = QMenu(self)
+ menu.setObjectName("profileMenu")
+
+ a_new = menu.addAction("πŸ“„ New Profile")
+ menu.addSeparator()
+ a_open = menu.addAction("πŸ“‚ Load Profile…")
+ menu.addSeparator()
+ a_save = menu.addAction("πŸ’Ύ Save")
+ a_save_as = menu.addAction("πŸ’Ύ Save As…")
+
+ a_save.setEnabled(bool(self._current_path))
+
+ a_new.triggered.connect(self._do_new)
+ a_open.triggered.connect(self._do_load)
+ a_save.triggered.connect(self._do_save)
+ a_save_as.triggered.connect(self._do_save_as)
+
+ # Show below the button
+ pos = self.mapToGlobal(QPoint(0, self.height()))
+ menu.exec(pos)
+
+ # ── Actions ───────────────────────────────────────────────────────────────
+
+ def _do_new(self):
+ reply = QMessageBox.question(
+ self, "New Profile",
+ "Discard current configuration and start a blank profile?",
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
+ )
+ if reply == QMessageBox.StandardButton.Yes:
+ self._current_path = ""
+ self._on_new()
+ self._update_title()
+
+ def _do_load(self):
+ path, _ = QFileDialog.getOpenFileName(
+ self, "Load Profile", self._default_dir(),
+ PROFILE_FILTER,
+ )
+ if not path:
+ return
+ try:
+ profile = Profile.load(path)
+ self._apply_profile(profile)
+ self._current_path = path
+ self._update_title(profile.name)
+ except Exception as e:
+ QMessageBox.critical(self, "Load Failed", str(e))
+
+ def _do_save(self):
+ if not self._current_path:
+ self._do_save_as()
+ return
+ self._save_to(self._current_path)
+
+ def _do_save_as(self):
+ # Ask for profile name
+ name, ok = QInputDialog.getText(
+ self, "Profile Name", "Profile name:",
+ text=os.path.splitext(os.path.basename(self._current_path))[0]
+ if self._current_path else "My Profile",
+ )
+ if not ok or not name.strip():
+ return
+ name = name.strip()
+
+ path, _ = QFileDialog.getSaveFileName(
+ self, "Save Profile As",
+ os.path.join(self._default_dir(), name + PROFILE_EXT),
+ PROFILE_FILTER,
+ )
+ if not path:
+ return
+ if not path.endswith(PROFILE_EXT):
+ path += PROFILE_EXT
+ self._save_to(path, name)
+
+ def _save_to(self, path: str, name: str = ""):
+ try:
+ profile = self._get_profile(name or os.path.splitext(
+ os.path.basename(path))[0])
+ profile.save(path)
+ self._current_path = path
+ self._update_title(profile.name)
+ except Exception as e:
+ QMessageBox.critical(self, "Save Failed", str(e))
+
+ # ── Helpers ───────────────────────────────────────────────────────────────
+
+ def _default_dir(self) -> str:
+ d = os.path.join(os.path.expanduser("~"), "labdaq_profiles")
+ os.makedirs(d, exist_ok=True)
+ return d
+
+ def _update_title(self, name: str = ""):
+ app = QApplication.instance()
+ if app and hasattr(app, "topLevelWidgets"):
+ for w in app.topLevelWidgets():
+ if hasattr(w, "setWindowTitle") and "LabDAQ" in (w.windowTitle() or ""):
+ title = "LabDAQ"
+ if name:
+ title += f" β€” {name}"
+ if self._current_path:
+ title += f" [{os.path.basename(self._current_path)}]"
+ w.setWindowTitle(title)
+ break