diff options
Diffstat (limited to 'ui/profile_manager_ui.py')
| -rw-r--r-- | ui/profile_manager_ui.py | 152 |
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 |
