summaryrefslogtreecommitdiff
path: root/ui/profile_manager_ui.py
blob: 8966f8f580933ae5d077b8fb27423b50ac0e9c09 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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