bec-widgets 0.93.2__py3-none-any.whl → 0.93.4__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 +32 -34
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +9 -16
- bec_widgets/qt_utils/settings_dialog.py +11 -0
- bec_widgets/widgets/color_button/color_button.py +7 -0
- bec_widgets/widgets/dock/dock.py +4 -1
- bec_widgets/widgets/dock/dock_area.py +19 -5
- bec_widgets/widgets/figure/figure.py +21 -16
- bec_widgets/widgets/figure/plots/image/image.py +14 -0
- bec_widgets/widgets/figure/plots/plot_base.py +8 -0
- bec_widgets/widgets/image/image_widget.py +2 -0
- bec_widgets/widgets/motor_map/motor_map_dialog/motor_map_settings.py +9 -0
- bec_widgets/widgets/{device_box/device_box.py → positioner_box/positioner_box.py} +88 -11
- bec_widgets/widgets/positioner_box/positioner_box.pyproject +1 -0
- bec_widgets/widgets/{device_box/device_box.ui → positioner_box/positioner_box.ui} +20 -3
- bec_widgets/widgets/{device_box/device_box_plugin.py → positioner_box/positioner_box_plugin.py} +9 -14
- bec_widgets/widgets/{device_box/register_device_box.py → positioner_box/register_positioner_box.py} +2 -2
- bec_widgets/widgets/website/website.py +6 -0
- {bec_widgets-0.93.2.dist-info → bec_widgets-0.93.4.dist-info}/METADATA +1 -1
- {bec_widgets-0.93.2.dist-info → bec_widgets-0.93.4.dist-info}/RECORD +50 -50
- pyproject.toml +1 -1
- tests/end-2-end/test_scan_control_e2e.py +0 -1
- tests/unit_tests/conftest.py +33 -2
- tests/unit_tests/test_bec_dock.py +6 -5
- tests/unit_tests/test_bec_figure.py +31 -24
- tests/unit_tests/test_bec_image.py +7 -3
- tests/unit_tests/test_bec_image_widget.py +0 -1
- tests/unit_tests/test_bec_motor_map.py +28 -14
- tests/unit_tests/test_bec_queue.py +0 -1
- tests/unit_tests/test_bec_status_box.py +0 -1
- tests/unit_tests/test_color_map_selector.py +0 -1
- tests/unit_tests/test_device_input_base.py +0 -1
- tests/unit_tests/test_device_input_widgets.py +0 -6
- tests/unit_tests/test_motor_map_widget.py +0 -2
- tests/unit_tests/test_plot_base.py +9 -4
- tests/unit_tests/test_positioner_box.py +104 -0
- tests/unit_tests/test_ring_progress_bar.py +0 -1
- tests/unit_tests/test_scan_control.py +0 -1
- tests/unit_tests/test_setting_dialog.py +1 -2
- tests/unit_tests/test_stop_button.py +0 -1
- tests/unit_tests/test_text_box_widget.py +0 -1
- tests/unit_tests/test_toggle.py +0 -1
- tests/unit_tests/test_vscode_widget.py +2 -2
- tests/unit_tests/test_waveform1d.py +46 -23
- tests/unit_tests/test_waveform_widget.py +0 -1
- tests/unit_tests/test_website_widget.py +0 -2
- bec_widgets/widgets/device_box/device_box.pyproject +0 -1
- tests/unit_tests/test_device_box.py +0 -98
- /bec_widgets/widgets/{device_box → positioner_box}/__init__.py +0 -0
- {bec_widgets-0.93.2.dist-info → bec_widgets-0.93.4.dist-info}/WHEEL +0 -0
- {bec_widgets-0.93.2.dist-info → bec_widgets-0.93.4.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.93.2.dist-info → bec_widgets-0.93.4.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
@@ -1,5 +1,37 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## v0.93.4 (2024-08-07)
|
4
|
+
|
5
|
+
### Fix
|
6
|
+
|
7
|
+
* fix: rename DeviceBox to PositionerBox, fix test for validation ([`37aa371`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/37aa371e7c4c62d70abf37abc125db0c088790fe))
|
8
|
+
|
9
|
+
* fix: add validation for bec_lib.device.Positioner; closes #268 ([`eb54e9f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/eb54e9f788e97af23db8fe0c78f8facb8688bb99))
|
10
|
+
|
11
|
+
## v0.93.3 (2024-08-07)
|
12
|
+
|
13
|
+
### Fix
|
14
|
+
|
15
|
+
* fix(dock): properly shut down docks and temp areas ([`99ee545`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/99ee545e41c6078654958b668b5b329f85553d16))
|
16
|
+
|
17
|
+
* fix(settings): shut down settings dialog ([`b50b3a2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b50b3a27e68956e10e8169a0aa698c911d2d9642))
|
18
|
+
|
19
|
+
* fix(website): fixed teardown of website widgets ([`a3d4f5a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a3d4f5ac4bc52acfed2791a1724fade6972ed320))
|
20
|
+
|
21
|
+
* fix(dock): properly shut down docks and dock areas ([`bc26497`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/bc264975b1363c9dfea516621d7878c320677d15))
|
22
|
+
|
23
|
+
* fix(figure): cleanup pyqtgraph ([`ad07bbf`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ad07bbf85e9c8d9838bdd686f69d41c235b7db19))
|
24
|
+
|
25
|
+
### Test
|
26
|
+
|
27
|
+
* test: removed quit from teardown ([`cf94599`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/cf94599c2544d6831c8afbe7b340082077557ed1))
|
28
|
+
|
29
|
+
* test: removed explicit call to close the widget ([`bf6294e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/bf6294ecbfd494565d2dc215e4d7e0c280ac7745))
|
30
|
+
|
31
|
+
* test: use factory instead of fixture to properly cleanup widgets on teardown ([`9856857`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/9856857f4cc7fa229c10d00fbae4452464a207cb))
|
32
|
+
|
33
|
+
* test: ensure all toplevelwidgets are closed ([`f9e5897`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f9e58979009cf632feea529700ad191401dd7eb8))
|
34
|
+
|
3
35
|
## v0.93.2 (2024-08-07)
|
4
36
|
|
5
37
|
### Fix
|
@@ -104,10 +136,6 @@ This reverts commit fd6ae91993a23a7b8dbb2cf3c4b7c3eda6d2b0f6 ([`5aad401`](https:
|
|
104
136
|
|
105
137
|
* feat(dock_area): plugin added ([`a16b87a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a16b87ac28d164230dd2e8020f50ff3a63cd407e))
|
106
138
|
|
107
|
-
* feat(dock_area): Added toolbar to dock area to add widgets without CLI interactions ([`cce1367`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/cce1367a72fca7206d351894bd1831b7bbfa7ec6))
|
108
|
-
|
109
|
-
* feat(toolbar): expandable menu actions ([`28f26e9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/28f26e92a46063db1a194be552156a5d3b2c43e7))
|
110
|
-
|
111
139
|
### Fix
|
112
140
|
|
113
141
|
* fix(status_item): icons changed to material design ([`1b9c55a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1b9c55a46a0dfd8678c8e95ff64dd6e8cfb9233e))
|
@@ -117,33 +145,3 @@ This reverts commit fd6ae91993a23a7b8dbb2cf3c4b7c3eda6d2b0f6 ([`5aad401`](https:
|
|
117
145
|
### Test
|
118
146
|
|
119
147
|
* test(dock_area): tests extended ([`06fab0e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/06fab0eab926cef5677d4988fd1fce09da342dd8))
|
120
|
-
|
121
|
-
## v0.90.0 (2024-07-23)
|
122
|
-
|
123
|
-
### Feature
|
124
|
-
|
125
|
-
* feat(image_widget): plugin added ([`4371168`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/43711680ba253f81fb0ffe764bcaae701b02bb49))
|
126
|
-
|
127
|
-
* feat(image_widget): all toolbar actions added ([`501eb92`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/501eb923f12fa6aaa93f5428ca78e57694edfbc0))
|
128
|
-
|
129
|
-
* feat(image_widget): image_widget added ([`6a9317f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6a9317facda896ee784c7fc1db0cd3d68cdfcf73))
|
130
|
-
|
131
|
-
### Fix
|
132
|
-
|
133
|
-
* fix(axis_setting): fix compatibility for issue with horizontal line for PyQt6 ([`1cf6e32`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1cf6e32303f82bc7c3f3391d0e96a88bc31f29fc))
|
134
|
-
|
135
|
-
* fix(image_widget): image_widget autorange fixed ([`7f49893`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7f49893d2ce3b9d02efa764f7f10442ed6ab8f3c))
|
136
|
-
|
137
|
-
* fix(image_widget): image widget adjusted ([`3d2ca48`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/3d2ca4855c36fe0af59a4b540caa3c8023a81773))
|
138
|
-
|
139
|
-
* fix(image): only single monitor image is allowed ([`fe7e542`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fe7e542b19dc5b401523501acb74ac03edf62ad4))
|
140
|
-
|
141
|
-
* fix(image): raw data are saved in image item to always have precise processing ([`c15035b`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c15035b6b769a96780a16da9e7f75af3b823654c))
|
142
|
-
|
143
|
-
### Refactor
|
144
|
-
|
145
|
-
* refactor(jupyter_console_example): added examples of standalone widgets ([`ba0d1ea`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ba0d1ea9031b4ae2e2e73bf269fbfad973b924a5))
|
146
|
-
|
147
|
-
### Test
|
148
|
-
|
149
|
-
* test(image_widget): tests added ([`70fb276`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/70fb276fdf31dffc105435d3dfe7c5caea0b10ce))
|
PKG-INFO
CHANGED
bec_widgets/cli/client.py
CHANGED
@@ -21,9 +21,9 @@ class Widgets(str, enum.Enum):
|
|
21
21
|
BECQueue = "BECQueue"
|
22
22
|
BECStatusBox = "BECStatusBox"
|
23
23
|
BECWaveformWidget = "BECWaveformWidget"
|
24
|
-
DeviceBox = "DeviceBox"
|
25
24
|
DeviceComboBox = "DeviceComboBox"
|
26
25
|
DeviceLineEdit = "DeviceLineEdit"
|
26
|
+
PositionerBox = "PositionerBox"
|
27
27
|
RingProgressBar = "RingProgressBar"
|
28
28
|
ScanControl = "ScanControl"
|
29
29
|
StopButton = "StopButton"
|
@@ -2287,7 +2287,7 @@ class BECWaveformWidget(RPCBase):
|
|
2287
2287
|
"""
|
2288
2288
|
|
2289
2289
|
|
2290
|
-
class
|
2290
|
+
class DeviceComboBox(RPCBase):
|
2291
2291
|
@property
|
2292
2292
|
@rpc_call
|
2293
2293
|
def _config_dict(self) -> "dict":
|
@@ -2305,7 +2305,7 @@ class DeviceBox(RPCBase):
|
|
2305
2305
|
"""
|
2306
2306
|
|
2307
2307
|
|
2308
|
-
class
|
2308
|
+
class DeviceInputBase(RPCBase):
|
2309
2309
|
@property
|
2310
2310
|
@rpc_call
|
2311
2311
|
def _config_dict(self) -> "dict":
|
@@ -2323,7 +2323,7 @@ class DeviceComboBox(RPCBase):
|
|
2323
2323
|
"""
|
2324
2324
|
|
2325
2325
|
|
2326
|
-
class
|
2326
|
+
class DeviceLineEdit(RPCBase):
|
2327
2327
|
@property
|
2328
2328
|
@rpc_call
|
2329
2329
|
def _config_dict(self) -> "dict":
|
@@ -2341,21 +2341,14 @@ class DeviceInputBase(RPCBase):
|
|
2341
2341
|
"""
|
2342
2342
|
|
2343
2343
|
|
2344
|
-
class
|
2345
|
-
@property
|
2344
|
+
class PositionerBox(RPCBase):
|
2346
2345
|
@rpc_call
|
2347
|
-
def
|
2348
|
-
"""
|
2349
|
-
Get the configuration of the widget.
|
2350
|
-
|
2351
|
-
Returns:
|
2352
|
-
dict: The configuration of the widget.
|
2346
|
+
def set_positioner(self, positioner: str):
|
2353
2347
|
"""
|
2348
|
+
Set the device
|
2354
2349
|
|
2355
|
-
|
2356
|
-
|
2357
|
-
"""
|
2358
|
-
Get all registered RPC objects.
|
2350
|
+
Args:
|
2351
|
+
positioner (Positioner | str) : Positioner to set, accepts str or the device
|
2359
2352
|
"""
|
2360
2353
|
|
2361
2354
|
|
@@ -106,3 +106,14 @@ class SettingsDialog(QDialog):
|
|
106
106
|
Apply the changes made in the settings widget without closing the dialog.
|
107
107
|
"""
|
108
108
|
self.widget.accept_changes()
|
109
|
+
|
110
|
+
def cleanup(self):
|
111
|
+
"""
|
112
|
+
Cleanup the dialog.
|
113
|
+
"""
|
114
|
+
self.button_box.close()
|
115
|
+
self.button_box.deleteLater()
|
116
|
+
|
117
|
+
def closeEvent(self, event):
|
118
|
+
self.cleanup()
|
119
|
+
super().closeEvent(event)
|
bec_widgets/widgets/dock/dock.py
CHANGED
@@ -153,6 +153,7 @@ class BECDock(BECWidget, Dock):
|
|
153
153
|
super().dropEvent(event)
|
154
154
|
if old_area in self.orig_area.tempAreas and old_area != self.orig_area:
|
155
155
|
self.orig_area.removeTempArea(old_area)
|
156
|
+
old_area.window().deleteLater()
|
156
157
|
|
157
158
|
def float(self):
|
158
159
|
"""
|
@@ -284,7 +285,7 @@ class BECDock(BECWidget, Dock):
|
|
284
285
|
"""
|
285
286
|
Attach the dock to the parent dock area.
|
286
287
|
"""
|
287
|
-
self.
|
288
|
+
self.parent_dock_area.remove_temp_area(self.area)
|
288
289
|
|
289
290
|
def detach(self):
|
290
291
|
"""
|
@@ -319,6 +320,8 @@ class BECDock(BECWidget, Dock):
|
|
319
320
|
if hasattr(widget, "cleanup"):
|
320
321
|
widget.cleanup()
|
321
322
|
self.widgets.clear()
|
323
|
+
self.label.close()
|
324
|
+
self.label.deleteLater()
|
322
325
|
super().cleanup()
|
323
326
|
|
324
327
|
def close(self):
|
@@ -83,8 +83,8 @@ class BECDockArea(BECWidget, QWidget):
|
|
83
83
|
"scan_control": IconAction(
|
84
84
|
icon_path="scan_control.svg", tooltip="Add Scan Control"
|
85
85
|
),
|
86
|
-
"
|
87
|
-
icon_path="
|
86
|
+
"positioner_box": IconAction(
|
87
|
+
icon_path="positioner_box.svg", tooltip="Add Device Box"
|
88
88
|
),
|
89
89
|
},
|
90
90
|
),
|
@@ -132,8 +132,8 @@ class BECDockArea(BECWidget, QWidget):
|
|
132
132
|
self.toolbar.widgets["menu_devices"].widgets["scan_control"].triggered.connect(
|
133
133
|
lambda: self.add_dock(widget="ScanControl", prefix="scan_control")
|
134
134
|
)
|
135
|
-
self.toolbar.widgets["menu_devices"].widgets["
|
136
|
-
lambda: self.add_dock(widget="
|
135
|
+
self.toolbar.widgets["menu_devices"].widgets["positioner_box"].triggered.connect(
|
136
|
+
lambda: self.add_dock(widget="PositionerBox", prefix="positioner_box")
|
137
137
|
)
|
138
138
|
|
139
139
|
# Menu Utils
|
@@ -231,6 +231,7 @@ class BECDockArea(BECWidget, QWidget):
|
|
231
231
|
self.config.docks.pop(name, None)
|
232
232
|
if dock:
|
233
233
|
dock.close()
|
234
|
+
dock.deleteLater()
|
234
235
|
if len(self.dock_area.docks) <= 1:
|
235
236
|
for dock in self.dock_area.docks.values():
|
236
237
|
dock.hide_title_bar()
|
@@ -329,7 +330,16 @@ class BECDockArea(BECWidget, QWidget):
|
|
329
330
|
"""
|
330
331
|
while self.dock_area.tempAreas:
|
331
332
|
for temp_area in self.dock_area.tempAreas:
|
332
|
-
self.
|
333
|
+
self.remove_temp_area(temp_area)
|
334
|
+
|
335
|
+
def remove_temp_area(self, area):
|
336
|
+
"""
|
337
|
+
Remove a temporary area from the dock area.
|
338
|
+
This is a patched method of pyqtgraph's removeTempArea
|
339
|
+
"""
|
340
|
+
self.dock_area.tempAreas.remove(area)
|
341
|
+
area.window().close()
|
342
|
+
area.window().deleteLater()
|
333
343
|
|
334
344
|
def clear_all(self):
|
335
345
|
"""
|
@@ -345,6 +355,10 @@ class BECDockArea(BECWidget, QWidget):
|
|
345
355
|
Cleanup the dock area.
|
346
356
|
"""
|
347
357
|
self.clear_all()
|
358
|
+
self.toolbar.close()
|
359
|
+
self.toolbar.deleteLater()
|
360
|
+
self.dock_area.close()
|
361
|
+
self.dock_area.deleteLater()
|
348
362
|
super().cleanup()
|
349
363
|
|
350
364
|
def close(self):
|
@@ -513,6 +513,13 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
|
513
513
|
if widget_id in self._widgets:
|
514
514
|
raise ValueError(f"Widget with ID '{widget_id}' already exists.")
|
515
515
|
|
516
|
+
# Check if position is occupied
|
517
|
+
if row is not None and col is not None:
|
518
|
+
if self.getItem(row, col):
|
519
|
+
raise ValueError(f"Position at row {row} and column {col} is already occupied.")
|
520
|
+
else:
|
521
|
+
row, col = self._find_next_empty_position()
|
522
|
+
|
516
523
|
widget = self.widget_handler.create_widget(
|
517
524
|
widget_type=widget_type,
|
518
525
|
widget_id=widget_id,
|
@@ -525,23 +532,11 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
|
525
532
|
# used otherwise multiple times
|
526
533
|
widget.set_gui_id(widget_id)
|
527
534
|
|
528
|
-
|
529
|
-
|
530
|
-
if self.getItem(row, col):
|
531
|
-
raise ValueError(f"Position at row {row} and column {col} is already occupied.")
|
532
|
-
|
533
|
-
widget.config.row = row
|
534
|
-
widget.config.col = col
|
535
|
-
|
536
|
-
# Add widget to the figure
|
537
|
-
self.addItem(widget, row=row, col=col)
|
538
|
-
else:
|
539
|
-
row, col = self._find_next_empty_position()
|
540
|
-
widget.config.row = row
|
541
|
-
widget.config.col = col
|
535
|
+
widget.config.row = row
|
536
|
+
widget.config.col = col
|
542
537
|
|
543
|
-
|
544
|
-
|
538
|
+
# Add widget to the figure
|
539
|
+
self.addItem(widget, row=row, col=col)
|
545
540
|
|
546
541
|
# Update num_cols and num_rows based on the added widget
|
547
542
|
self.config.num_rows = max(self.config.num_rows, row + 1)
|
@@ -620,6 +615,7 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
|
620
615
|
"""
|
621
616
|
if widget_id in self._widgets:
|
622
617
|
widget = self._widgets.pop(widget_id)
|
618
|
+
widget.cleanup_pyqtgraph()
|
623
619
|
widget.cleanup()
|
624
620
|
self.removeItem(widget)
|
625
621
|
self.grid[widget.config.row][widget.config.col] = None
|
@@ -745,3 +741,12 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
|
745
741
|
self.config = FigureConfig(
|
746
742
|
widget_class=self.__class__.__name__, gui_id=self.gui_id, theme=theme
|
747
743
|
)
|
744
|
+
|
745
|
+
def cleanup_pyqtgraph_all_widgets(self):
|
746
|
+
"""Clean up the pyqtgraph widget."""
|
747
|
+
for widget in self.widget_list:
|
748
|
+
widget.cleanup_pyqtgraph()
|
749
|
+
|
750
|
+
def cleanup(self):
|
751
|
+
"""Close the figure widget."""
|
752
|
+
self.cleanup_pyqtgraph_all_widgets()
|
@@ -681,3 +681,17 @@ class BECImageShow(BECPlotBase):
|
|
681
681
|
self.on_image_update, MessageEndpoints.device_monitor_2d(monitor)
|
682
682
|
)
|
683
683
|
self.images.clear()
|
684
|
+
|
685
|
+
def cleanup_pyqtgraph(self):
|
686
|
+
"""Cleanup pyqtgraph items."""
|
687
|
+
super().cleanup_pyqtgraph()
|
688
|
+
item = self.plot_item
|
689
|
+
if not item.items:
|
690
|
+
return
|
691
|
+
cbar = item.items[0].color_bar
|
692
|
+
cbar.vb.menu.close()
|
693
|
+
cbar.vb.menu.deleteLater()
|
694
|
+
cbar.gradient.menu.close()
|
695
|
+
cbar.gradient.menu.deleteLater()
|
696
|
+
cbar.gradient.colorDialog.close()
|
697
|
+
cbar.gradient.colorDialog.deleteLater()
|
@@ -314,3 +314,11 @@ class BECPlotBase(BECConnector, pg.GraphicsLayout):
|
|
314
314
|
"""Remove the plot widget from the figure."""
|
315
315
|
if self.figure is not None:
|
316
316
|
self.figure.remove(widget_id=self.gui_id)
|
317
|
+
|
318
|
+
def cleanup_pyqtgraph(self):
|
319
|
+
"""Cleanup pyqtgraph items."""
|
320
|
+
item = self.plot_item
|
321
|
+
item.vb.menu.close()
|
322
|
+
item.vb.menu.deleteLater()
|
323
|
+
item.ctrlMenu.close()
|
324
|
+
item.ctrlMenu.deleteLater()
|
@@ -45,3 +45,12 @@ class MotorMapSettings(SettingWidget):
|
|
45
45
|
self.target_widget.set_scatter_size(scatter_size)
|
46
46
|
self.target_widget.set_background_value(background_intensity)
|
47
47
|
self.target_widget.set_color(color)
|
48
|
+
|
49
|
+
def cleanup(self):
|
50
|
+
self.ui.color.cleanup()
|
51
|
+
self.ui.color.close()
|
52
|
+
self.ui.color.deleteLater()
|
53
|
+
|
54
|
+
def closeEvent(self, event):
|
55
|
+
self.cleanup()
|
56
|
+
super().closeEvent(event)
|
@@ -1,7 +1,11 @@
|
|
1
|
+
""" Module for a PositionerBox widget to control a positioner device."""
|
2
|
+
|
1
3
|
import os
|
2
4
|
import uuid
|
3
5
|
|
6
|
+
from bec_lib.device import Positioner
|
4
7
|
from bec_lib.endpoints import MessageEndpoints
|
8
|
+
from bec_lib.logger import bec_logger
|
5
9
|
from bec_lib.messages import ScanQueueMessage
|
6
10
|
from qtpy.QtCore import Property, Signal, Slot
|
7
11
|
from qtpy.QtGui import QDoubleValidator
|
@@ -11,12 +15,23 @@ from bec_widgets.utils import UILoader
|
|
11
15
|
from bec_widgets.utils.bec_widget import BECWidget
|
12
16
|
from bec_widgets.utils.colors import apply_theme
|
13
17
|
|
18
|
+
logger = bec_logger.logger
|
19
|
+
|
20
|
+
|
21
|
+
class PositionerBox(BECWidget, QWidget):
|
22
|
+
"""Simple Widget to control a positioner in box form"""
|
14
23
|
|
15
|
-
|
24
|
+
USER_ACCESS = ["set_positioner"]
|
16
25
|
device_changed = Signal(str, str)
|
17
26
|
|
18
|
-
def __init__(self, parent=None, device=None, *args, **kwargs):
|
19
|
-
|
27
|
+
def __init__(self, parent=None, device: Positioner = None, *args, **kwargs):
|
28
|
+
"""Initialize the PositionerBox widget.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
parent: The parent widget.
|
32
|
+
device (Positioner): The device to control.
|
33
|
+
"""
|
34
|
+
super().__init__(**kwargs)
|
20
35
|
QWidget.__init__(self, parent=parent)
|
21
36
|
self.get_bec_shortcuts()
|
22
37
|
self._device = ""
|
@@ -29,10 +44,11 @@ class DeviceBox(BECWidget, QWidget):
|
|
29
44
|
self.init_device()
|
30
45
|
|
31
46
|
def init_ui(self):
|
47
|
+
"""Init the ui"""
|
32
48
|
self.device_changed.connect(self.on_device_change)
|
33
49
|
|
34
50
|
current_path = os.path.dirname(__file__)
|
35
|
-
self.ui = UILoader(self).loader(os.path.join(current_path, "
|
51
|
+
self.ui = UILoader(self).loader(os.path.join(current_path, "positioner_box.ui"))
|
36
52
|
|
37
53
|
self.layout = QVBoxLayout(self)
|
38
54
|
self.layout.addWidget(self.ui)
|
@@ -57,28 +73,73 @@ class DeviceBox(BECWidget, QWidget):
|
|
57
73
|
self.ui.spinner_widget.start()
|
58
74
|
|
59
75
|
def init_device(self):
|
60
|
-
|
76
|
+
"""Init the device view and readback"""
|
77
|
+
if self._check_device_is_valid(self.device):
|
61
78
|
data = self.dev[self.device].read()
|
62
79
|
self.on_device_readback({"signals": data}, {})
|
63
80
|
|
81
|
+
def _toogle_enable_buttons(self, enable: bool) -> None:
|
82
|
+
"""Toogle enable/disable on available buttons
|
83
|
+
|
84
|
+
Args:
|
85
|
+
enable (bool): Enable buttons
|
86
|
+
"""
|
87
|
+
self.ui.tweak_left.setEnabled(enable)
|
88
|
+
self.ui.tweak_right.setEnabled(enable)
|
89
|
+
self.ui.stop.setEnabled(enable)
|
90
|
+
self.ui.setpoint.setEnabled(enable)
|
91
|
+
self.ui.step_size.setEnabled(enable)
|
92
|
+
|
64
93
|
@Property(str)
|
65
94
|
def device(self):
|
95
|
+
"""Property to set the device"""
|
66
96
|
return self._device
|
67
97
|
|
68
98
|
@device.setter
|
69
|
-
def device(self, value):
|
99
|
+
def device(self, value: str):
|
100
|
+
"""Setter, checks if device is a string"""
|
70
101
|
if not value or not isinstance(value, str):
|
71
102
|
return
|
72
103
|
old_device = self._device
|
73
104
|
self._device = value
|
74
105
|
self.device_changed.emit(old_device, value)
|
75
106
|
|
107
|
+
def set_positioner(self, positioner: str):
|
108
|
+
"""Set the device
|
109
|
+
|
110
|
+
Args:
|
111
|
+
positioner (Positioner | str) : Positioner to set, accepts str or the device
|
112
|
+
"""
|
113
|
+
if isinstance(positioner, Positioner):
|
114
|
+
positioner = positioner.name
|
115
|
+
self.device = positioner
|
116
|
+
|
117
|
+
def _check_device_is_valid(self, device: str):
|
118
|
+
"""Check if the device is a positioner
|
119
|
+
|
120
|
+
Args:
|
121
|
+
device (str): The device name
|
122
|
+
"""
|
123
|
+
if device not in self.dev:
|
124
|
+
logger.info(f"Device {device} not found in the device list")
|
125
|
+
return False
|
126
|
+
if not isinstance(self.dev[device], Positioner):
|
127
|
+
logger.info(f"Device {device} is not a positioner")
|
128
|
+
return False
|
129
|
+
return True
|
130
|
+
|
76
131
|
@Slot(str, str)
|
77
132
|
def on_device_change(self, old_device: str, new_device: str):
|
78
|
-
if
|
79
|
-
|
133
|
+
"""Upon changing the device, a check will be performed if the device is a Positioner.
|
134
|
+
|
135
|
+
Args:
|
136
|
+
old_device (str): The old device name.
|
137
|
+
new_device (str): The new device name.
|
138
|
+
"""
|
139
|
+
if not self._check_device_is_valid(new_device):
|
80
140
|
return
|
81
|
-
|
141
|
+
logger.info(f"Device changed from {old_device} to {new_device}")
|
142
|
+
self._toogle_enable_buttons(True)
|
82
143
|
self.init_device()
|
83
144
|
self.bec_dispatcher.disconnect_slot(
|
84
145
|
self.on_device_readback, MessageEndpoints.device_readback(old_device)
|
@@ -98,6 +159,12 @@ class DeviceBox(BECWidget, QWidget):
|
|
98
159
|
|
99
160
|
@Slot(dict, dict)
|
100
161
|
def on_device_readback(self, msg_content: dict, metadata: dict):
|
162
|
+
"""Callback for device readback.
|
163
|
+
|
164
|
+
Args:
|
165
|
+
msg_content (dict): The message content.
|
166
|
+
metadata (dict): The message metadata.
|
167
|
+
"""
|
101
168
|
signals = msg_content.get("signals", {})
|
102
169
|
# pylint: disable=protected-access
|
103
170
|
hinted_signals = self.dev[self.device]._hints
|
@@ -134,7 +201,12 @@ class DeviceBox(BECWidget, QWidget):
|
|
134
201
|
pos = (readback_val - limits[0]) / (limits[1] - limits[0])
|
135
202
|
self.ui.position_indicator.on_position_update(pos)
|
136
203
|
|
137
|
-
def update_limits(self, limits):
|
204
|
+
def update_limits(self, limits: tuple):
|
205
|
+
"""Update limits
|
206
|
+
|
207
|
+
Args:
|
208
|
+
limits (tuple): Limits of the positioner
|
209
|
+
"""
|
138
210
|
if limits == self._limits:
|
139
211
|
return
|
140
212
|
self._limits = limits
|
@@ -147,6 +219,7 @@ class DeviceBox(BECWidget, QWidget):
|
|
147
219
|
|
148
220
|
@Slot()
|
149
221
|
def on_stop(self):
|
222
|
+
"""Stop call"""
|
150
223
|
request_id = str(uuid.uuid4())
|
151
224
|
params = {
|
152
225
|
"device": self.device,
|
@@ -165,18 +238,22 @@ class DeviceBox(BECWidget, QWidget):
|
|
165
238
|
|
166
239
|
@property
|
167
240
|
def step_size(self):
|
241
|
+
"""Step size for tweak"""
|
168
242
|
return self.ui.step_size.value()
|
169
243
|
|
170
244
|
@Slot()
|
171
245
|
def on_tweak_right(self):
|
246
|
+
"""Tweak motor right"""
|
172
247
|
self.dev[self.device].move(self.step_size, relative=True)
|
173
248
|
|
174
249
|
@Slot()
|
175
250
|
def on_tweak_left(self):
|
251
|
+
"""Tweak motor left"""
|
176
252
|
self.dev[self.device].move(-self.step_size, relative=True)
|
177
253
|
|
178
254
|
@Slot()
|
179
255
|
def on_setpoint_change(self):
|
256
|
+
"""Change the setpoint for the motor"""
|
180
257
|
self.ui.setpoint.clearFocus()
|
181
258
|
setpoint = self.ui.setpoint.text()
|
182
259
|
self.dev[self.device].move(float(setpoint), relative=False)
|
@@ -191,7 +268,7 @@ if __name__ == "__main__": # pragma: no cover
|
|
191
268
|
|
192
269
|
app = QApplication(sys.argv)
|
193
270
|
apply_theme("light")
|
194
|
-
widget =
|
271
|
+
widget = PositionerBox(device="bpm4i")
|
195
272
|
|
196
273
|
widget.show()
|
197
274
|
sys.exit(app.exec_())
|
@@ -0,0 +1 @@
|
|
1
|
+
{'files': ['positioner_box.py']}
|
@@ -29,17 +29,24 @@
|
|
29
29
|
<item>
|
30
30
|
<widget class="QGroupBox" name="device_box">
|
31
31
|
<property name="title">
|
32
|
-
<string>
|
32
|
+
<string>No positioner selected</string>
|
33
33
|
</property>
|
34
34
|
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0">
|
35
35
|
<property name="topMargin">
|
36
36
|
<number>0</number>
|
37
37
|
</property>
|
38
38
|
<item row="3" column="1">
|
39
|
-
<widget class="QDoubleSpinBox" name="step_size"
|
39
|
+
<widget class="QDoubleSpinBox" name="step_size">
|
40
|
+
<property name="enabled">
|
41
|
+
<bool>false</bool>
|
42
|
+
</property>
|
43
|
+
</widget>
|
40
44
|
</item>
|
41
45
|
<item row="3" column="2">
|
42
46
|
<widget class="QToolButton" name="tweak_right">
|
47
|
+
<property name="enabled">
|
48
|
+
<bool>false</bool>
|
49
|
+
</property>
|
43
50
|
<property name="minimumSize">
|
44
51
|
<size>
|
45
52
|
<width>50</width>
|
@@ -67,10 +74,17 @@
|
|
67
74
|
</widget>
|
68
75
|
</item>
|
69
76
|
<item row="2" column="0" colspan="3">
|
70
|
-
<widget class="QLineEdit" name="setpoint"
|
77
|
+
<widget class="QLineEdit" name="setpoint">
|
78
|
+
<property name="enabled">
|
79
|
+
<bool>false</bool>
|
80
|
+
</property>
|
81
|
+
</widget>
|
71
82
|
</item>
|
72
83
|
<item row="3" column="0">
|
73
84
|
<widget class="QToolButton" name="tweak_left">
|
85
|
+
<property name="enabled">
|
86
|
+
<bool>false</bool>
|
87
|
+
</property>
|
74
88
|
<property name="minimumSize">
|
75
89
|
<size>
|
76
90
|
<width>50</width>
|
@@ -99,6 +113,9 @@
|
|
99
113
|
</item>
|
100
114
|
<item row="4" column="0" colspan="3">
|
101
115
|
<widget class="QPushButton" name="stop">
|
116
|
+
<property name="enabled">
|
117
|
+
<bool>false</bool>
|
118
|
+
</property>
|
102
119
|
<property name="text">
|
103
120
|
<string>Stop</string>
|
104
121
|
</property>
|