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.
Files changed (52) hide show
  1. CHANGELOG.md +32 -34
  2. PKG-INFO +1 -1
  3. bec_widgets/cli/client.py +9 -16
  4. bec_widgets/qt_utils/settings_dialog.py +11 -0
  5. bec_widgets/widgets/color_button/color_button.py +7 -0
  6. bec_widgets/widgets/dock/dock.py +4 -1
  7. bec_widgets/widgets/dock/dock_area.py +19 -5
  8. bec_widgets/widgets/figure/figure.py +21 -16
  9. bec_widgets/widgets/figure/plots/image/image.py +14 -0
  10. bec_widgets/widgets/figure/plots/plot_base.py +8 -0
  11. bec_widgets/widgets/image/image_widget.py +2 -0
  12. bec_widgets/widgets/motor_map/motor_map_dialog/motor_map_settings.py +9 -0
  13. bec_widgets/widgets/{device_box/device_box.py → positioner_box/positioner_box.py} +88 -11
  14. bec_widgets/widgets/positioner_box/positioner_box.pyproject +1 -0
  15. bec_widgets/widgets/{device_box/device_box.ui → positioner_box/positioner_box.ui} +20 -3
  16. bec_widgets/widgets/{device_box/device_box_plugin.py → positioner_box/positioner_box_plugin.py} +9 -14
  17. bec_widgets/widgets/{device_box/register_device_box.py → positioner_box/register_positioner_box.py} +2 -2
  18. bec_widgets/widgets/website/website.py +6 -0
  19. {bec_widgets-0.93.2.dist-info → bec_widgets-0.93.4.dist-info}/METADATA +1 -1
  20. {bec_widgets-0.93.2.dist-info → bec_widgets-0.93.4.dist-info}/RECORD +50 -50
  21. pyproject.toml +1 -1
  22. tests/end-2-end/test_scan_control_e2e.py +0 -1
  23. tests/unit_tests/conftest.py +33 -2
  24. tests/unit_tests/test_bec_dock.py +6 -5
  25. tests/unit_tests/test_bec_figure.py +31 -24
  26. tests/unit_tests/test_bec_image.py +7 -3
  27. tests/unit_tests/test_bec_image_widget.py +0 -1
  28. tests/unit_tests/test_bec_motor_map.py +28 -14
  29. tests/unit_tests/test_bec_queue.py +0 -1
  30. tests/unit_tests/test_bec_status_box.py +0 -1
  31. tests/unit_tests/test_color_map_selector.py +0 -1
  32. tests/unit_tests/test_device_input_base.py +0 -1
  33. tests/unit_tests/test_device_input_widgets.py +0 -6
  34. tests/unit_tests/test_motor_map_widget.py +0 -2
  35. tests/unit_tests/test_plot_base.py +9 -4
  36. tests/unit_tests/test_positioner_box.py +104 -0
  37. tests/unit_tests/test_ring_progress_bar.py +0 -1
  38. tests/unit_tests/test_scan_control.py +0 -1
  39. tests/unit_tests/test_setting_dialog.py +1 -2
  40. tests/unit_tests/test_stop_button.py +0 -1
  41. tests/unit_tests/test_text_box_widget.py +0 -1
  42. tests/unit_tests/test_toggle.py +0 -1
  43. tests/unit_tests/test_vscode_widget.py +2 -2
  44. tests/unit_tests/test_waveform1d.py +46 -23
  45. tests/unit_tests/test_waveform_widget.py +0 -1
  46. tests/unit_tests/test_website_widget.py +0 -2
  47. bec_widgets/widgets/device_box/device_box.pyproject +0 -1
  48. tests/unit_tests/test_device_box.py +0 -98
  49. /bec_widgets/widgets/{device_box → positioner_box}/__init__.py +0 -0
  50. {bec_widgets-0.93.2.dist-info → bec_widgets-0.93.4.dist-info}/WHEEL +0 -0
  51. {bec_widgets-0.93.2.dist-info → bec_widgets-0.93.4.dist-info}/entry_points.txt +0 -0
  52. {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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.93.2
3
+ Version: 0.93.4
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
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 DeviceBox(RPCBase):
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 DeviceComboBox(RPCBase):
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 DeviceInputBase(RPCBase):
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 DeviceLineEdit(RPCBase):
2345
- @property
2344
+ class PositionerBox(RPCBase):
2346
2345
  @rpc_call
2347
- def _config_dict(self) -> "dict":
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
- @rpc_call
2356
- def _get_all_rpc(self) -> "dict":
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)
@@ -34,3 +34,10 @@ class ColorButton(pg.ColorButton):
34
34
  return self.color().getRgb()
35
35
  if format == "HEX":
36
36
  return self.color().name()
37
+
38
+ def cleanup(self):
39
+ """
40
+ Clean up the ColorButton.
41
+ """
42
+ self.colorDialog.close()
43
+ self.colorDialog.deleteLater()
@@ -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.orig_area.removeTempArea(self.area)
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
- "device_box": IconAction(
87
- icon_path="device_box.svg", tooltip="Add Device Box"
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["device_box"].triggered.connect(
136
- lambda: self.add_dock(widget="DeviceBox", prefix="device_box")
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.dock_area.removeTempArea(temp_area)
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
- # Check if position is occupied
529
- if row is not None and col is not None:
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
- # Add widget to the figure
544
- self.addItem(widget, row=row, col=col)
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()
@@ -458,6 +458,8 @@ class BECImageWidget(BECWidget, QWidget):
458
458
  def cleanup(self):
459
459
  self.fig.cleanup()
460
460
  self.client.shutdown()
461
+ self.toolbar.close()
462
+ self.toolbar.deleteLater()
461
463
  return super().cleanup()
462
464
 
463
465
 
@@ -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
- class DeviceBox(BECWidget, QWidget):
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
- super().__init__(*args, **kwargs)
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, "device_box.ui"))
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
- if self.device in self.dev:
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 new_device not in self.dev:
79
- print(f"Device {new_device} not found in the device list")
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
- print(f"Device changed from {old_device} to {new_device}")
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 = DeviceBox(device="samx")
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>Device Name</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>