bec-widgets 2.15.1__py3-none-any.whl → 2.16.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CHANGELOG.md +61 -0
- PKG-INFO +1 -1
- bec_widgets/tests/utils.py +2 -2
- bec_widgets/utils/expandable_frame.py +12 -7
- bec_widgets/utils/forms_from_types/forms.py +40 -22
- bec_widgets/utils/forms_from_types/items.py +282 -32
- bec_widgets/widgets/editors/dict_backed_table.py +69 -9
- bec_widgets/widgets/editors/scan_metadata/_util.py +3 -1
- bec_widgets/widgets/services/device_browser/device_browser.py +5 -6
- bec_widgets/widgets/services/device_browser/device_item/device_config_dialog.py +254 -0
- bec_widgets/widgets/services/device_browser/device_item/device_config_form.py +60 -0
- bec_widgets/widgets/services/device_browser/device_item/device_item.py +52 -54
- bec_widgets/widgets/utility/toggle/toggle.py +9 -0
- {bec_widgets-2.15.1.dist-info → bec_widgets-2.16.0.dist-info}/METADATA +1 -1
- {bec_widgets-2.15.1.dist-info → bec_widgets-2.16.0.dist-info}/RECORD +19 -17
- pyproject.toml +1 -1
- {bec_widgets-2.15.1.dist-info → bec_widgets-2.16.0.dist-info}/WHEEL +0 -0
- {bec_widgets-2.15.1.dist-info → bec_widgets-2.16.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-2.15.1.dist-info → bec_widgets-2.16.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,254 @@
|
|
1
|
+
from ast import literal_eval
|
2
|
+
|
3
|
+
from bec_lib.atlas_models import Device as DeviceConfigModel
|
4
|
+
from bec_lib.config_helper import CONF as DEVICE_CONF_KEYS
|
5
|
+
from bec_lib.config_helper import ConfigHelper
|
6
|
+
from bec_lib.logger import bec_logger
|
7
|
+
from qtpy.QtCore import QObject, QRunnable, QSize, Qt, QThreadPool, Signal
|
8
|
+
from qtpy.QtWidgets import (
|
9
|
+
QApplication,
|
10
|
+
QDialog,
|
11
|
+
QDialogButtonBox,
|
12
|
+
QLabel,
|
13
|
+
QStackedLayout,
|
14
|
+
QVBoxLayout,
|
15
|
+
QWidget,
|
16
|
+
)
|
17
|
+
|
18
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
19
|
+
from bec_widgets.utils.error_popups import SafeSlot
|
20
|
+
from bec_widgets.widgets.services.device_browser.device_item.device_config_form import (
|
21
|
+
DeviceConfigForm,
|
22
|
+
)
|
23
|
+
from bec_widgets.widgets.utility.spinner.spinner import SpinnerWidget
|
24
|
+
|
25
|
+
logger = bec_logger.logger
|
26
|
+
|
27
|
+
|
28
|
+
class _CommSignals(QObject):
|
29
|
+
error = Signal(Exception)
|
30
|
+
done = Signal()
|
31
|
+
|
32
|
+
|
33
|
+
class _CommunicateUpdate(QRunnable):
|
34
|
+
|
35
|
+
def __init__(self, config_helper: ConfigHelper, device: str, config: dict) -> None:
|
36
|
+
super().__init__()
|
37
|
+
self.config_helper = config_helper
|
38
|
+
self.device = device
|
39
|
+
self.config = config
|
40
|
+
self.signals = _CommSignals()
|
41
|
+
|
42
|
+
@SafeSlot()
|
43
|
+
def run(self):
|
44
|
+
try:
|
45
|
+
timeout = self.config_helper.suggested_timeout_s(self.config)
|
46
|
+
RID = self.config_helper.send_config_request(
|
47
|
+
action="update", config={self.device: self.config}, wait_for_response=False
|
48
|
+
)
|
49
|
+
logger.info("Waiting for config reply")
|
50
|
+
reply = self.config_helper.wait_for_config_reply(RID, timeout=timeout)
|
51
|
+
self.config_helper.handle_update_reply(reply, RID, timeout)
|
52
|
+
logger.info("Done updating config!")
|
53
|
+
except Exception as e:
|
54
|
+
self.signals.error.emit(e)
|
55
|
+
finally:
|
56
|
+
self.signals.done.emit()
|
57
|
+
|
58
|
+
|
59
|
+
class DeviceConfigDialog(BECWidget, QDialog):
|
60
|
+
RPC = False
|
61
|
+
applied = Signal()
|
62
|
+
|
63
|
+
def __init__(
|
64
|
+
self,
|
65
|
+
parent=None,
|
66
|
+
device: str | None = None,
|
67
|
+
config_helper: ConfigHelper | None = None,
|
68
|
+
**kwargs,
|
69
|
+
):
|
70
|
+
super().__init__(parent=parent, **kwargs)
|
71
|
+
self._config_helper = config_helper or ConfigHelper(
|
72
|
+
self.client.connector, self.client._service_name
|
73
|
+
)
|
74
|
+
self.threadpool = QThreadPool()
|
75
|
+
self._device = device
|
76
|
+
self.setWindowTitle(f"Edit config for: {device}")
|
77
|
+
self._container = QStackedLayout()
|
78
|
+
self._container.setStackingMode(QStackedLayout.StackAll)
|
79
|
+
|
80
|
+
self._layout = QVBoxLayout()
|
81
|
+
user_warning = QLabel(
|
82
|
+
"Warning: edit items here at your own risk - minimal validation is applied to the entered values.\n"
|
83
|
+
"Items in the deviceConfig dictionary should correspond to python literals, e.g. numbers, lists, strings (including quotes), etc."
|
84
|
+
)
|
85
|
+
user_warning.setWordWrap(True)
|
86
|
+
user_warning.setStyleSheet("QLabel { color: red; }")
|
87
|
+
self._layout.addWidget(user_warning)
|
88
|
+
self._add_form()
|
89
|
+
self._add_overlay()
|
90
|
+
self._add_buttons()
|
91
|
+
|
92
|
+
self.setLayout(self._container)
|
93
|
+
self._overlay_widget.setVisible(False)
|
94
|
+
|
95
|
+
def _add_form(self):
|
96
|
+
self._form_widget = QWidget()
|
97
|
+
self._form_widget.setLayout(self._layout)
|
98
|
+
self._form = DeviceConfigForm()
|
99
|
+
self._layout.addWidget(self._form)
|
100
|
+
|
101
|
+
for row in self._form.enumerate_form_widgets():
|
102
|
+
if row.label.property("_model_field_name") in DEVICE_CONF_KEYS.NON_UPDATABLE:
|
103
|
+
row.widget._set_pretty_display()
|
104
|
+
|
105
|
+
self._fetch_config()
|
106
|
+
self._fill_form()
|
107
|
+
self._container.addWidget(self._form_widget)
|
108
|
+
|
109
|
+
def _add_overlay(self):
|
110
|
+
self._overlay_widget = QWidget()
|
111
|
+
self._overlay_widget.setStyleSheet("background-color:rgba(128,128,128,128);")
|
112
|
+
self._overlay_widget.setAutoFillBackground(True)
|
113
|
+
self._overlay_layout = QVBoxLayout()
|
114
|
+
self._overlay_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
115
|
+
self._overlay_widget.setLayout(self._overlay_layout)
|
116
|
+
|
117
|
+
self._spinner = SpinnerWidget(parent=self)
|
118
|
+
self._spinner.setMinimumSize(QSize(100, 100))
|
119
|
+
self._overlay_layout.addWidget(self._spinner)
|
120
|
+
self._container.addWidget(self._overlay_widget)
|
121
|
+
|
122
|
+
def _add_buttons(self):
|
123
|
+
button_box = QDialogButtonBox(
|
124
|
+
QDialogButtonBox.Apply | QDialogButtonBox.Ok | QDialogButtonBox.Cancel
|
125
|
+
)
|
126
|
+
button_box.button(QDialogButtonBox.Apply).clicked.connect(self.apply)
|
127
|
+
button_box.accepted.connect(self.accept)
|
128
|
+
button_box.rejected.connect(self.reject)
|
129
|
+
self._layout.addWidget(button_box)
|
130
|
+
|
131
|
+
def _fetch_config(self):
|
132
|
+
self._initial_config = {}
|
133
|
+
if (
|
134
|
+
self.client.device_manager is not None
|
135
|
+
and self._device in self.client.device_manager.devices
|
136
|
+
):
|
137
|
+
self._initial_config = self.client.device_manager.devices.get(self._device)._config
|
138
|
+
|
139
|
+
def _fill_form(self):
|
140
|
+
self._form.set_data(DeviceConfigModel.model_validate(self._initial_config))
|
141
|
+
|
142
|
+
def updated_config(self):
|
143
|
+
new_config = self._form.get_form_data()
|
144
|
+
diff = {
|
145
|
+
k: v for k, v in new_config.items() if self._initial_config.get(k) != new_config.get(k)
|
146
|
+
}
|
147
|
+
if diff.get("deviceConfig") is not None:
|
148
|
+
# TODO: special cased in some parts of device manager but not others, should
|
149
|
+
# be removed in config update as with below issue
|
150
|
+
diff["deviceConfig"].pop("device_access", None)
|
151
|
+
# TODO: replace when https://github.com/bec-project/bec/issues/528 is resolved
|
152
|
+
diff["deviceConfig"] = {
|
153
|
+
k: literal_eval(str(v)) for k, v in diff["deviceConfig"].items()
|
154
|
+
}
|
155
|
+
return diff
|
156
|
+
|
157
|
+
@SafeSlot()
|
158
|
+
def apply(self):
|
159
|
+
self._process_update_action()
|
160
|
+
self.applied.emit()
|
161
|
+
|
162
|
+
@SafeSlot()
|
163
|
+
def accept(self):
|
164
|
+
self._process_update_action()
|
165
|
+
return super().accept()
|
166
|
+
|
167
|
+
def _process_update_action(self):
|
168
|
+
updated_config = self.updated_config()
|
169
|
+
if (device_name := updated_config.get("name")) == "":
|
170
|
+
logger.warning("Can't create a device with no name!")
|
171
|
+
elif set(updated_config.keys()) & set(DEVICE_CONF_KEYS.NON_UPDATABLE):
|
172
|
+
logger.info(
|
173
|
+
f"Removing old device {self._device} and adding new device {device_name or self._device} with modified config: {updated_config}"
|
174
|
+
)
|
175
|
+
else:
|
176
|
+
self._update_device_config(updated_config)
|
177
|
+
|
178
|
+
def _update_device_config(self, config: dict):
|
179
|
+
if self._device is None:
|
180
|
+
return
|
181
|
+
if config == {}:
|
182
|
+
logger.info("No changes made to device config")
|
183
|
+
return
|
184
|
+
logger.info(f"Sending request to update device config: {config}")
|
185
|
+
|
186
|
+
self._start_waiting_display()
|
187
|
+
communicate_update = _CommunicateUpdate(self._config_helper, self._device, config)
|
188
|
+
communicate_update.signals.error.connect(self.update_error)
|
189
|
+
communicate_update.signals.done.connect(self.update_done)
|
190
|
+
self.threadpool.start(communicate_update)
|
191
|
+
|
192
|
+
@SafeSlot()
|
193
|
+
def update_done(self):
|
194
|
+
self._stop_waiting_display()
|
195
|
+
self._fetch_config()
|
196
|
+
self._fill_form()
|
197
|
+
|
198
|
+
@SafeSlot(Exception, popup_error=True)
|
199
|
+
def update_error(self, e: Exception):
|
200
|
+
raise RuntimeError("Failed to update device configuration") from e
|
201
|
+
|
202
|
+
def _start_waiting_display(self):
|
203
|
+
self._overlay_widget.setVisible(True)
|
204
|
+
self._spinner.start()
|
205
|
+
QApplication.processEvents()
|
206
|
+
|
207
|
+
def _stop_waiting_display(self):
|
208
|
+
self._overlay_widget.setVisible(False)
|
209
|
+
self._spinner.stop()
|
210
|
+
QApplication.processEvents()
|
211
|
+
|
212
|
+
|
213
|
+
def main(): # pragma: no cover
|
214
|
+
import sys
|
215
|
+
|
216
|
+
from qtpy.QtWidgets import QApplication, QLineEdit, QPushButton, QWidget
|
217
|
+
|
218
|
+
from bec_widgets.utils.colors import set_theme
|
219
|
+
|
220
|
+
dialog = None
|
221
|
+
|
222
|
+
app = QApplication(sys.argv)
|
223
|
+
set_theme("light")
|
224
|
+
widget = QWidget()
|
225
|
+
widget.setLayout(QVBoxLayout())
|
226
|
+
|
227
|
+
device = QLineEdit()
|
228
|
+
widget.layout().addWidget(device)
|
229
|
+
|
230
|
+
def _destroy_dialog(*_):
|
231
|
+
nonlocal dialog
|
232
|
+
dialog = None
|
233
|
+
|
234
|
+
def accept(*args):
|
235
|
+
logger.success(f"submitted device config form {dialog} {args}")
|
236
|
+
_destroy_dialog()
|
237
|
+
|
238
|
+
def _show_dialog(*_):
|
239
|
+
nonlocal dialog
|
240
|
+
if dialog is None:
|
241
|
+
dialog = DeviceConfigDialog(device=device.text())
|
242
|
+
dialog.accepted.connect(accept)
|
243
|
+
dialog.rejected.connect(_destroy_dialog)
|
244
|
+
dialog.open()
|
245
|
+
|
246
|
+
button = QPushButton("Show device dialog")
|
247
|
+
widget.layout().addWidget(button)
|
248
|
+
button.clicked.connect(_show_dialog)
|
249
|
+
widget.show()
|
250
|
+
sys.exit(app.exec_())
|
251
|
+
|
252
|
+
|
253
|
+
if __name__ == "__main__":
|
254
|
+
main()
|
@@ -0,0 +1,60 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from bec_lib.atlas_models import Device as DeviceConfigModel
|
4
|
+
from pydantic import BaseModel
|
5
|
+
from qtpy.QtWidgets import QApplication
|
6
|
+
|
7
|
+
from bec_widgets.utils.colors import get_theme_name
|
8
|
+
from bec_widgets.utils.forms_from_types import styles
|
9
|
+
from bec_widgets.utils.forms_from_types.forms import PydanticModelForm
|
10
|
+
from bec_widgets.utils.forms_from_types.items import (
|
11
|
+
DEFAULT_WIDGET_TYPES,
|
12
|
+
BoolFormItem,
|
13
|
+
BoolToggleFormItem,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
class DeviceConfigForm(PydanticModelForm):
|
18
|
+
RPC = False
|
19
|
+
PLUGIN = False
|
20
|
+
|
21
|
+
def __init__(self, parent=None, client=None, pretty_display=False, **kwargs):
|
22
|
+
super().__init__(
|
23
|
+
parent=parent,
|
24
|
+
data_model=DeviceConfigModel,
|
25
|
+
pretty_display=pretty_display,
|
26
|
+
client=client,
|
27
|
+
**kwargs,
|
28
|
+
)
|
29
|
+
self._widget_types = DEFAULT_WIDGET_TYPES.copy()
|
30
|
+
self._widget_types["bool"] = (lambda spec: spec.item_type is bool, BoolToggleFormItem)
|
31
|
+
self._widget_types["optional_bool"] = (
|
32
|
+
lambda spec: spec.item_type == bool | None,
|
33
|
+
BoolFormItem,
|
34
|
+
)
|
35
|
+
self._validity.setVisible(False)
|
36
|
+
self._connect_to_theme_change()
|
37
|
+
self.populate()
|
38
|
+
|
39
|
+
def _post_init(self): ...
|
40
|
+
|
41
|
+
def set_pretty_display_theme(self, theme: str | None = None):
|
42
|
+
if theme is None:
|
43
|
+
theme = get_theme_name()
|
44
|
+
self.setStyleSheet(styles.pretty_display_theme(theme))
|
45
|
+
|
46
|
+
def get_form_data(self):
|
47
|
+
"""Get the entered metadata as a dict."""
|
48
|
+
return self._md_schema.model_validate(super().get_form_data()).model_dump()
|
49
|
+
|
50
|
+
def _connect_to_theme_change(self):
|
51
|
+
"""Connect to the theme change signal."""
|
52
|
+
qapp = QApplication.instance()
|
53
|
+
if hasattr(qapp, "theme_signal"):
|
54
|
+
qapp.theme_signal.theme_updated.connect(self.set_pretty_display_theme) # type: ignore
|
55
|
+
|
56
|
+
def set_schema(self, schema: type[BaseModel]):
|
57
|
+
raise TypeError("This class doesn't support changing the schema")
|
58
|
+
|
59
|
+
def set_data(self, data: DeviceConfigModel): # type: ignore # This class locks the type
|
60
|
+
super().set_data(data)
|
@@ -3,49 +3,27 @@ from __future__ import annotations
|
|
3
3
|
from typing import TYPE_CHECKING
|
4
4
|
|
5
5
|
from bec_lib.atlas_models import Device as DeviceConfigModel
|
6
|
+
from bec_lib.devicemanager import DeviceContainer
|
6
7
|
from bec_lib.logger import bec_logger
|
8
|
+
from bec_qthemes import material_icon
|
7
9
|
from qtpy.QtCore import QMimeData, QSize, Qt, Signal
|
8
10
|
from qtpy.QtGui import QDrag
|
9
|
-
from qtpy.QtWidgets import QApplication, QHBoxLayout, QWidget
|
11
|
+
from qtpy.QtWidgets import QApplication, QHBoxLayout, QToolButton, QWidget
|
10
12
|
|
11
|
-
from bec_widgets.utils.colors import get_theme_name
|
12
13
|
from bec_widgets.utils.error_popups import SafeSlot
|
13
14
|
from bec_widgets.utils.expandable_frame import ExpandableGroupFrame
|
14
|
-
from bec_widgets.
|
15
|
-
|
16
|
-
|
15
|
+
from bec_widgets.widgets.services.device_browser.device_item.device_config_dialog import (
|
16
|
+
DeviceConfigDialog,
|
17
|
+
)
|
18
|
+
from bec_widgets.widgets.services.device_browser.device_item.device_config_form import (
|
19
|
+
DeviceConfigForm,
|
20
|
+
)
|
17
21
|
|
18
22
|
if TYPE_CHECKING: # pragma: no cover
|
19
23
|
from qtpy.QtGui import QMouseEvent
|
20
24
|
|
21
|
-
logger = bec_logger.logger
|
22
|
-
|
23
|
-
|
24
|
-
class DeviceItemForm(PydanticModelForm):
|
25
|
-
RPC = False
|
26
|
-
PLUGIN = False
|
27
|
-
|
28
|
-
def __init__(self, parent=None, client=None, pretty_display=False, **kwargs):
|
29
|
-
super().__init__(
|
30
|
-
parent=parent,
|
31
|
-
data_model=DeviceConfigModel,
|
32
|
-
pretty_display=pretty_display,
|
33
|
-
client=client,
|
34
|
-
**kwargs,
|
35
|
-
)
|
36
|
-
self._validity.setVisible(False)
|
37
|
-
self._connect_to_theme_change()
|
38
|
-
|
39
|
-
def set_pretty_display_theme(self, theme: str | None = None):
|
40
|
-
if theme is None:
|
41
|
-
theme = get_theme_name()
|
42
|
-
self.setStyleSheet(styles.pretty_display_theme(theme))
|
43
25
|
|
44
|
-
|
45
|
-
"""Connect to the theme change signal."""
|
46
|
-
qapp = QApplication.instance()
|
47
|
-
if hasattr(qapp, "theme_signal"):
|
48
|
-
qapp.theme_signal.theme_updated.connect(self.set_pretty_display_theme) # type: ignore
|
26
|
+
logger = bec_logger.logger
|
49
27
|
|
50
28
|
|
51
29
|
class DeviceItem(ExpandableGroupFrame):
|
@@ -53,9 +31,9 @@ class DeviceItem(ExpandableGroupFrame):
|
|
53
31
|
|
54
32
|
RPC = False
|
55
33
|
|
56
|
-
def __init__(self, parent, device: str, icon: str = "") -> None:
|
34
|
+
def __init__(self, parent, device: str, devices: DeviceContainer, icon: str = "") -> None:
|
57
35
|
super().__init__(parent, title=device, expanded=False, icon=icon)
|
58
|
-
|
36
|
+
self.dev = devices
|
59
37
|
self._drag_pos = None
|
60
38
|
self._expanded_first_time = False
|
61
39
|
self._data = None
|
@@ -65,17 +43,29 @@ class DeviceItem(ExpandableGroupFrame):
|
|
65
43
|
self.set_layout(layout)
|
66
44
|
|
67
45
|
self.adjustSize()
|
68
|
-
|
69
|
-
|
46
|
+
|
47
|
+
def _create_title_layout(self, title: str, icon: str):
|
48
|
+
super()._create_title_layout(title, icon)
|
49
|
+
self.edit_button = QToolButton()
|
50
|
+
self.edit_button.setIcon(
|
51
|
+
material_icon(icon_name="edit", size=(10, 10), convert_to_pixmap=False)
|
52
|
+
)
|
53
|
+
self._title_layout.insertWidget(self._title_layout.count() - 1, self.edit_button)
|
54
|
+
self.edit_button.clicked.connect(self._create_edit_dialog)
|
55
|
+
|
56
|
+
def _create_edit_dialog(self):
|
57
|
+
dialog = DeviceConfigDialog(parent=self, device=self.device)
|
58
|
+
dialog.accepted.connect(self._reload_config)
|
59
|
+
dialog.applied.connect(self._reload_config)
|
60
|
+
dialog.open()
|
70
61
|
|
71
62
|
@SafeSlot()
|
72
63
|
def switch_expanded_state(self):
|
73
64
|
if not self.expanded and not self._expanded_first_time:
|
74
65
|
self._expanded_first_time = True
|
75
|
-
self.form =
|
66
|
+
self.form = DeviceConfigForm(parent=self, pretty_display=True)
|
76
67
|
self._contents.layout().addWidget(self.form)
|
77
|
-
|
78
|
-
self.form.set_data(self._data)
|
68
|
+
self._reload_config()
|
79
69
|
self.broadcast_size_hint.emit(self.sizeHint())
|
80
70
|
super().switch_expanded_state()
|
81
71
|
if self._expanded_first_time:
|
@@ -86,6 +76,10 @@ class DeviceItem(ExpandableGroupFrame):
|
|
86
76
|
self.adjustSize()
|
87
77
|
self.broadcast_size_hint.emit(self.sizeHint())
|
88
78
|
|
79
|
+
@SafeSlot(popup_error=True)
|
80
|
+
def _reload_config(self, *_):
|
81
|
+
self.set_display_config(self.dev[self.device]._config)
|
82
|
+
|
89
83
|
def set_display_config(self, config_dict: dict):
|
90
84
|
"""Set the displayed information from a device config dict, which must conform to the
|
91
85
|
bec_lib.atlas_models.Device config model."""
|
@@ -118,29 +112,33 @@ class DeviceItem(ExpandableGroupFrame):
|
|
118
112
|
|
119
113
|
if __name__ == "__main__": # pragma: no cover
|
120
114
|
import sys
|
115
|
+
from unittest.mock import MagicMock
|
121
116
|
|
122
117
|
from qtpy.QtWidgets import QApplication
|
123
118
|
|
119
|
+
from bec_widgets.widgets.services.device_browser.device_item.device_config_form import (
|
120
|
+
DeviceConfigForm,
|
121
|
+
)
|
122
|
+
from bec_widgets.widgets.utility.visual.dark_mode_button.dark_mode_button import DarkModeButton
|
123
|
+
|
124
124
|
app = QApplication(sys.argv)
|
125
125
|
widget = QWidget()
|
126
126
|
layout = QHBoxLayout()
|
127
127
|
widget.setLayout(layout)
|
128
|
-
|
128
|
+
mock_config = {
|
129
|
+
"name": "Test Device",
|
130
|
+
"enabled": True,
|
131
|
+
"deviceClass": "FakeDeviceClass",
|
132
|
+
"deviceConfig": {"kwarg1": "value1"},
|
133
|
+
"readoutPriority": "baseline",
|
134
|
+
"description": "A device for testing out a widget",
|
135
|
+
"readOnly": True,
|
136
|
+
"softwareTrigger": False,
|
137
|
+
"deviceTags": {"tag1", "tag2", "tag3"},
|
138
|
+
"userParameter": {"some_setting": "some_ value"},
|
139
|
+
}
|
140
|
+
item = DeviceItem(widget, "Device", {"Device": MagicMock(enabled=True, _config=mock_config)})
|
129
141
|
layout.addWidget(DarkModeButton())
|
130
142
|
layout.addWidget(item)
|
131
|
-
item.set_display_config(
|
132
|
-
{
|
133
|
-
"name": "Test Device",
|
134
|
-
"enabled": True,
|
135
|
-
"deviceClass": "FakeDeviceClass",
|
136
|
-
"deviceConfig": {"kwarg1": "value1"},
|
137
|
-
"readoutPriority": "baseline",
|
138
|
-
"description": "A device for testing out a widget",
|
139
|
-
"readOnly": True,
|
140
|
-
"softwareTrigger": False,
|
141
|
-
"deviceTags": ["tag1", "tag2", "tag3"],
|
142
|
-
"userParameter": {"some_setting": "some_ value"},
|
143
|
-
}
|
144
|
-
)
|
145
143
|
widget.show()
|
146
144
|
sys.exit(app.exec_())
|
@@ -10,6 +10,7 @@ class ToggleSwitch(QWidget):
|
|
10
10
|
A simple toggle.
|
11
11
|
"""
|
12
12
|
|
13
|
+
stateChanged = Signal(bool)
|
13
14
|
enabled = Signal(bool)
|
14
15
|
ICON_NAME = "toggle_on"
|
15
16
|
PLUGIN = True
|
@@ -42,11 +43,19 @@ class ToggleSwitch(QWidget):
|
|
42
43
|
|
43
44
|
@checked.setter
|
44
45
|
def checked(self, state):
|
46
|
+
if self._checked != state:
|
47
|
+
self.stateChanged.emit(state)
|
45
48
|
self._checked = state
|
46
49
|
self.update_colors()
|
47
50
|
self.set_thumb_pos_to_state()
|
48
51
|
self.enabled.emit(self._checked)
|
49
52
|
|
53
|
+
def setChecked(self, state: bool):
|
54
|
+
self.checked = state
|
55
|
+
|
56
|
+
def isChecked(self):
|
57
|
+
return self.checked
|
58
|
+
|
50
59
|
@Property(QPointF)
|
51
60
|
def thumb_pos(self):
|
52
61
|
return self._thumb_pos
|
@@ -2,11 +2,11 @@
|
|
2
2
|
.gitlab-ci.yml,sha256=1nMYldzVk0tFkBWYTcUjumOrdSADASheWOAc0kOFDYs,9509
|
3
3
|
.pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
|
4
4
|
.readthedocs.yaml,sha256=ivqg3HTaOxNbEW3bzWh9MXAkrekuGoNdj0Mj3SdRYuw,639
|
5
|
-
CHANGELOG.md,sha256=
|
5
|
+
CHANGELOG.md,sha256=_jkBL9IyzlQ1Qeke_iNWW1uyEOUTnub8cOk6L16Th60,303426
|
6
6
|
LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
|
7
|
-
PKG-INFO,sha256=
|
7
|
+
PKG-INFO,sha256=Ruu7v-DGBzfToXWxY60FteqGE2djyfYs5FpnQk7oQYE,1252
|
8
8
|
README.md,sha256=oY5Jc1uXehRASuwUJ0umin2vfkFh7tHF-LLruHTaQx0,3560
|
9
|
-
pyproject.toml,sha256=
|
9
|
+
pyproject.toml,sha256=ZeBsXAfaSDFXfcgpGwnAY7zfZm58l6SkdU0AFL1f_uw,2827
|
10
10
|
.git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
|
11
11
|
.github/pull_request_template.md,sha256=F_cJXzooWMFgMGtLK-7KeGcQt0B4AYFse5oN0zQ9p6g,801
|
12
12
|
.github/ISSUE_TEMPLATE/bug_report.yml,sha256=WdRnt7HGxvsIBLzhkaOWNfg8IJQYa_oV9_F08Ym6znQ,1081
|
@@ -59,7 +59,7 @@ bec_widgets/examples/plugin_example_pyside/tictactoe.py,sha256=s3rCurXloVcmMdzZi
|
|
59
59
|
bec_widgets/examples/plugin_example_pyside/tictactoeplugin.py,sha256=MFMwONn4EZ3V8DboEG4I3BXpURE9JDbKB7XTzzfZl5w,1978
|
60
60
|
bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py,sha256=SiJaoX3OYA8YMkSwU1d7KEfSUjQQUsQgpRAxSSlr8oQ,2376
|
61
61
|
bec_widgets/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
62
|
-
bec_widgets/tests/utils.py,sha256=
|
62
|
+
bec_widgets/tests/utils.py,sha256=R04KpeQctQoDrtGRd00lrLIHxTbjMelrgIsE-Ea40_s,7151
|
63
63
|
bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
|
64
64
|
bec_widgets/utils/bec_connector.py,sha256=ATOSyZqryn1QHPc7aotiDnUtzFhlj_gmcukMT_pqjHQ,19272
|
65
65
|
bec_widgets/utils/bec_designer.py,sha256=ehNl_i743rijmhPiIGNd1bihE7-l4oJzTVoa4yjPjls,5426
|
@@ -76,7 +76,7 @@ bec_widgets/utils/container_utils.py,sha256=J8YXombOlAPa3M8NGZdhntp2NirBu4raDEQZ
|
|
76
76
|
bec_widgets/utils/crosshair.py,sha256=nqBPQqWzoTLZ-sPBR6ONm7M1TtGGD2EpRwm2iSNpoFo,22304
|
77
77
|
bec_widgets/utils/entry_validator.py,sha256=lwT8HP0RDG1FXENIeZ3IDEF2DQmD8KXGkRxPoMXbryk,1817
|
78
78
|
bec_widgets/utils/error_popups.py,sha256=UBAmD1YlAgKodpihudyf0VWtI59KGFiLgnjiKmKGjgk,13254
|
79
|
-
bec_widgets/utils/expandable_frame.py,sha256=
|
79
|
+
bec_widgets/utils/expandable_frame.py,sha256=BvR5kyALWPcOz7l5zarqG_M-EWFY2LMw0jfdDWC5-hI,4025
|
80
80
|
bec_widgets/utils/filter_io.py,sha256=GKO6GOTiKxTuTp_SHp5Ewou54N7wIZXgWWvlFvhbBmw,5114
|
81
81
|
bec_widgets/utils/fps_counter.py,sha256=seuCWwiNP5q2e2OEztloa66pNb3Sygh-0lEHAcYaDfc,2612
|
82
82
|
bec_widgets/utils/generate_designer_plugin.py,sha256=d_-cWQJ2s8Ff-XD_YQUgnVsRCMHBPcszXOAuJSeIWHM,5676
|
@@ -103,8 +103,8 @@ bec_widgets/utils/widget_io.py,sha256=afR7i59fw2WrEQCmkH5dwhz1uNjfMPnJTIhE8NWVBs
|
|
103
103
|
bec_widgets/utils/widget_state_manager.py,sha256=tzrxVmnGa6IHSEdeh-R68aQ934BsuS9nLQbwYQ78J90,7651
|
104
104
|
bec_widgets/utils/yaml_dialog.py,sha256=T6UyGNGdmpXW74fa_7Nk6b99T5pp2Wvyw3AOauRc8T8,2407
|
105
105
|
bec_widgets/utils/forms_from_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
106
|
-
bec_widgets/utils/forms_from_types/forms.py,sha256=
|
107
|
-
bec_widgets/utils/forms_from_types/items.py,sha256=
|
106
|
+
bec_widgets/utils/forms_from_types/forms.py,sha256=UhhuZNz0nZfPF9DERDYCq4Idp70NUcrgfAXpjn2G2Og,10928
|
107
|
+
bec_widgets/utils/forms_from_types/items.py,sha256=i9K8uoANKZuPuWPYSdK74UER3LXFf7sWHX1Rl-7ALgc,22458
|
108
108
|
bec_widgets/utils/forms_from_types/styles.py,sha256=PeMx8bLI2PX0--U1QOS-3yuGQiyAX4W3EpL_IA5iIrI,702
|
109
109
|
bec_widgets/utils/plugin_templates/plugin.template,sha256=DWtJdHpdsVtbiTTOniH3zBe5a40ztQ20o_-Hclyu38s,1266
|
110
110
|
bec_widgets/utils/plugin_templates/register.template,sha256=XyL3OZPT_FTArLAM8tHd5qMqv2ZuAbJAZLsNNnHcagU,417
|
@@ -222,11 +222,11 @@ bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog_compact.ui,sha256=JbdMEPzbA3p2
|
|
222
222
|
bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog_vertical.ui,sha256=BVm0XydqbgmIYOs3YPQzQsL4v4gZzourR10dH_2NrVg,5078
|
223
223
|
bec_widgets/widgets/dap/lmfit_dialog/register_lm_fit_dialog.py,sha256=7tB1gsvv310_kVuKf2u4EdSR4F1posm7QCrWH5Kih-Q,480
|
224
224
|
bec_widgets/widgets/editors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
225
|
-
bec_widgets/widgets/editors/dict_backed_table.py,sha256=
|
225
|
+
bec_widgets/widgets/editors/dict_backed_table.py,sha256=_ukL5J4DkPnGQPUAvPHwU9evv5yBBqwjhSNWrYwU6OI,9170
|
226
226
|
bec_widgets/widgets/editors/jupyter_console/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
227
227
|
bec_widgets/widgets/editors/jupyter_console/jupyter_console.py,sha256=-e7HQOECeH5eDrJYh4BFIzRL78LDkooU4otabyN0aX4,2343
|
228
228
|
bec_widgets/widgets/editors/scan_metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
229
|
-
bec_widgets/widgets/editors/scan_metadata/_util.py,sha256=
|
229
|
+
bec_widgets/widgets/editors/scan_metadata/_util.py,sha256=mKc_9y4jeul1jWU1G7fxjLJ4ANmtAAXUUgZUkzsGSyY,2090
|
230
230
|
bec_widgets/widgets/editors/scan_metadata/register_scan_metadata.py,sha256=dGcd8AXM1E9i-N45_CIRdCIyKtHJlq_7neyKHrDx6C0,487
|
231
231
|
bec_widgets/widgets/editors/scan_metadata/scan_metadata.py,sha256=t5sv6AgxxM4-Metgqh5OzOE5g7DNNvNZfBOjBvI8oh8,5445
|
232
232
|
bec_widgets/widgets/editors/scan_metadata/scan_metadata.pyproject,sha256=_4guiQAGxiQ4gSN5i5S160UnFXvPFC1W1nG77PubNYQ,31
|
@@ -349,14 +349,16 @@ bec_widgets/widgets/services/bec_status_box/bec_status_box_plugin.py,sha256=2tBP
|
|
349
349
|
bec_widgets/widgets/services/bec_status_box/register_bec_status_box.py,sha256=_1gr5nid6D5ZEvvt-GPyXhEGfacSh_QW0oWKxmZrUmY,490
|
350
350
|
bec_widgets/widgets/services/bec_status_box/status_item.py,sha256=CNhGk6J8iNvlldibcuy4PFQULRHvgqHyot-UJ8xfVJw,5609
|
351
351
|
bec_widgets/widgets/services/device_browser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
352
|
-
bec_widgets/widgets/services/device_browser/device_browser.py,sha256=
|
352
|
+
bec_widgets/widgets/services/device_browser/device_browser.py,sha256=RHJgOXDChAqy4BPpBVIKJReXXFJB-DpVvt3LhCFR9V8,5231
|
353
353
|
bec_widgets/widgets/services/device_browser/device_browser.pyproject,sha256=s0vM1xYUwyU6Je68_hSCSBLK8ZPfHB-ftt4flfdpkXY,32
|
354
354
|
bec_widgets/widgets/services/device_browser/device_browser.ui,sha256=Dy_3oXArScP-_7hRI6nQ7SKKnihuyYAzJfXiRxxJQBE,1077
|
355
355
|
bec_widgets/widgets/services/device_browser/device_browser_plugin.py,sha256=JS4uYQclT-h0jMTPZ_wZgi5MCert--gjmmBks_EmEoo,1299
|
356
356
|
bec_widgets/widgets/services/device_browser/register_device_browser.py,sha256=OrL6NwN47TMu-ib6vqtnQ3ogePMWVipG3IfEXyq2OqY,509
|
357
357
|
bec_widgets/widgets/services/device_browser/util.py,sha256=aDtRa53L4-CGn4rM9IKqUP80pBYBxh4pssE1Tc46tAo,370
|
358
358
|
bec_widgets/widgets/services/device_browser/device_item/__init__.py,sha256=VGY-uNVCnpcY-q-gijteB2N8KxFNgYR-qQ209MVu1QI,36
|
359
|
-
bec_widgets/widgets/services/device_browser/device_item/
|
359
|
+
bec_widgets/widgets/services/device_browser/device_item/device_config_dialog.py,sha256=183LABi4767PptQHy8mfV1I6MFlDGmC1vekFqjtkOuk,8748
|
360
|
+
bec_widgets/widgets/services/device_browser/device_item/device_config_form.py,sha256=Bw2sxvszs2qPe5WCUV_J4P6iuWSEQc0I37cyr9Wat30,2138
|
361
|
+
bec_widgets/widgets/services/device_browser/device_item/device_item.py,sha256=LxkeUphf6BjzQadOlyF080TUoPtFdivO7b7c-SjdK94,5251
|
360
362
|
bec_widgets/widgets/utility/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
361
363
|
bec_widgets/widgets/utility/logpanel/__init__.py,sha256=HldSvPLYgrqBjCgIQj0f7Wa4slkSMksk4bsRJOQi__Y,91
|
362
364
|
bec_widgets/widgets/utility/logpanel/_util.py,sha256=GqzHbdOTmWBru9OR4weeYdziWj_cWxqSJhS4_6W3Qjg,1836
|
@@ -380,7 +382,7 @@ bec_widgets/widgets/utility/spinner/spinner_widget.pyproject,sha256=zzLajGB3DTgV
|
|
380
382
|
bec_widgets/widgets/utility/spinner/spinner_widget_plugin.py,sha256=kRQCOwxPQsFOrJIkSoqq7xdF_VtoRo_b2vOZEr5eXrA,1363
|
381
383
|
bec_widgets/widgets/utility/toggle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
382
384
|
bec_widgets/widgets/utility/toggle/register_toggle_switch.py,sha256=6y9-Cr_qcpEphRK2_wyt6vWY9SnUzw78RHwZCW-xMRw,480
|
383
|
-
bec_widgets/widgets/utility/toggle/toggle.py,sha256=
|
385
|
+
bec_widgets/widgets/utility/toggle/toggle.py,sha256=NbbvemzDStIUaspVUUqWWdrpv_XhRbZsAmGJgYy1V_A,4748
|
384
386
|
bec_widgets/widgets/utility/toggle/toggle_switch.pyproject,sha256=Msa-AS5H5XlqW65r8GlX2AxD8FnFnDyDgGnbKcXqKOw,24
|
385
387
|
bec_widgets/widgets/utility/toggle/toggle_switch_plugin.py,sha256=rcazogplQXHu_4K2e9OTvfhOSUe4yi1kaTxN8a1GsXU,1352
|
386
388
|
bec_widgets/widgets/utility/visual/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -409,8 +411,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=O
|
|
409
411
|
bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
|
410
412
|
bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
|
411
413
|
bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
|
412
|
-
bec_widgets-2.
|
413
|
-
bec_widgets-2.
|
414
|
-
bec_widgets-2.
|
415
|
-
bec_widgets-2.
|
416
|
-
bec_widgets-2.
|
414
|
+
bec_widgets-2.16.0.dist-info/METADATA,sha256=Ruu7v-DGBzfToXWxY60FteqGE2djyfYs5FpnQk7oQYE,1252
|
415
|
+
bec_widgets-2.16.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
416
|
+
bec_widgets-2.16.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
|
417
|
+
bec_widgets-2.16.0.dist-info/licenses/LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
|
418
|
+
bec_widgets-2.16.0.dist-info/RECORD,,
|
pyproject.toml
CHANGED
File without changes
|
File without changes
|
File without changes
|