From 2a634dca0c7962b90004f75c4cdac6225201bb5e Mon Sep 17 00:00:00 2001 From: Christian Kolset Date: Tue, 21 Apr 2026 13:43:52 -0600 Subject: V13 This version contains the profile feature. Allowins users to save the channels, signals and plots for specific labs. --- ui/profile_manager_ui.py | 152 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 ui/profile_manager_ui.py (limited to 'ui/profile_manager_ui.py') 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 -- cgit v1.2.3