bec-widgets 1.16.5__py3-none-any.whl → 1.17.1__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 (34) hide show
  1. CHANGELOG.md +37 -0
  2. PKG-INFO +1 -1
  3. bec_widgets/cli/client.py +46 -0
  4. bec_widgets/utils/bec_signal_proxy.py +39 -11
  5. bec_widgets/widgets/containers/dock/dock_area.py +1 -1
  6. bec_widgets/widgets/control/device_control/positioner_box/__init__.py +11 -0
  7. bec_widgets/widgets/control/device_control/positioner_box/_base/__init__.py +3 -0
  8. bec_widgets/widgets/control/device_control/positioner_box/_base/positioner_box_base.py +243 -0
  9. bec_widgets/widgets/control/device_control/positioner_box/positioner_box/__init__.py +0 -0
  10. bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.py +242 -0
  11. bec_widgets/widgets/control/device_control/positioner_box/{positioner_box_plugin.py → positioner_box/positioner_box_plugin.py} +1 -1
  12. bec_widgets/widgets/control/device_control/positioner_box/{register_positioner_box.py → positioner_box/register_positioner_box.py} +1 -1
  13. bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/__init__.py +0 -0
  14. bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box2_d.pyproject +1 -0
  15. bec_widgets/widgets/control/{device_input/signal_combobox/signal_combo_box_plugin.py → device_control/positioner_box/positioner_box_2d/positioner_box2_d_plugin.py} +11 -9
  16. bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.py +482 -0
  17. bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.ui +562 -0
  18. bec_widgets/widgets/control/{device_input/signal_combobox/register_signal_combo_box.py → device_control/positioner_box/positioner_box_2d/register_positioner_box2_d.py} +3 -3
  19. bec_widgets/widgets/control/device_control/positioner_box/{positioner_control_line.py → positioner_control_line/positioner_control_line.py} +5 -2
  20. bec_widgets/widgets/control/device_control/positioner_box/{positioner_control_line_plugin.py → positioner_control_line/positioner_control_line_plugin.py} +1 -3
  21. bec_widgets/widgets/control/device_control/positioner_box/{register_positioner_control_line.py → positioner_control_line/register_positioner_control_line.py} +1 -1
  22. bec_widgets/widgets/control/device_control/positioner_group/positioner_group.py +7 -6
  23. {bec_widgets-1.16.5.dist-info → bec_widgets-1.17.1.dist-info}/METADATA +1 -1
  24. {bec_widgets-1.16.5.dist-info → bec_widgets-1.17.1.dist-info}/RECORD +32 -26
  25. pyproject.toml +1 -1
  26. bec_widgets/widgets/control/device_control/positioner_box/positioner_box.py +0 -352
  27. bec_widgets/widgets/control/device_input/signal_combobox/signal_combo_box.pyproject +0 -1
  28. /bec_widgets/widgets/control/device_control/positioner_box/{positioner_box.pyproject → positioner_box/positioner_box.pyproject} +0 -0
  29. /bec_widgets/widgets/control/device_control/positioner_box/{positioner_box.ui → positioner_box/positioner_box.ui} +0 -0
  30. /bec_widgets/widgets/control/device_control/positioner_box/{positioner_control_line.pyproject → positioner_control_line/positioner_control_line.pyproject} +0 -0
  31. /bec_widgets/widgets/control/device_control/positioner_box/{positioner_control_line.ui → positioner_control_line/positioner_control_line.ui} +0 -0
  32. {bec_widgets-1.16.5.dist-info → bec_widgets-1.17.1.dist-info}/WHEEL +0 -0
  33. {bec_widgets-1.16.5.dist-info → bec_widgets-1.17.1.dist-info}/entry_points.txt +0 -0
  34. {bec_widgets-1.16.5.dist-info → bec_widgets-1.17.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,242 @@
1
+ """ Module for a PositionerBox widget to control a positioner device."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+ from bec_lib.device import Positioner
8
+ from bec_lib.logger import bec_logger
9
+ from bec_qthemes import material_icon
10
+ from qtpy.QtCore import Signal
11
+ from qtpy.QtGui import QDoubleValidator
12
+ from qtpy.QtWidgets import QDoubleSpinBox
13
+
14
+ from bec_widgets.qt_utils.error_popups import SafeProperty, SafeSlot
15
+ from bec_widgets.utils import UILoader
16
+ from bec_widgets.utils.colors import get_accent_colors, set_theme
17
+ from bec_widgets.widgets.control.device_control.positioner_box._base import PositionerBoxBase
18
+ from bec_widgets.widgets.control.device_control.positioner_box._base.positioner_box_base import (
19
+ DeviceUpdateUIComponents,
20
+ )
21
+
22
+ logger = bec_logger.logger
23
+
24
+ MODULE_PATH = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
25
+
26
+
27
+ class PositionerBox(PositionerBoxBase):
28
+ """Simple Widget to control a positioner in box form"""
29
+
30
+ ui_file = "positioner_box.ui"
31
+ dimensions = (234, 224)
32
+
33
+ PLUGIN = True
34
+
35
+ USER_ACCESS = ["set_positioner"]
36
+ device_changed = Signal(str, str)
37
+ # Signal emitted to inform listeners about a position update
38
+ position_update = Signal(float)
39
+
40
+ def __init__(self, parent=None, device: Positioner | str | None = None, **kwargs):
41
+ """Initialize the PositionerBox widget.
42
+
43
+ Args:
44
+ parent: The parent widget.
45
+ device (Positioner): The device to control.
46
+ """
47
+ super().__init__(parent=parent, **kwargs)
48
+
49
+ self._device = ""
50
+ self._limits = None
51
+ if self.current_path == "":
52
+ self.current_path = os.path.dirname(__file__)
53
+
54
+ self.init_ui()
55
+ self.device = device
56
+ self._init_device(self.device, self.position_update.emit, self.update_limits)
57
+
58
+ def init_ui(self):
59
+ """Init the ui"""
60
+ self.device_changed.connect(self.on_device_change)
61
+
62
+ self.ui = UILoader(self).loader(os.path.join(self.current_path, self.ui_file))
63
+
64
+ self.addWidget(self.ui)
65
+ self.layout.setSpacing(0)
66
+ self.layout.setContentsMargins(0, 0, 0, 0)
67
+
68
+ # fix the size of the device box
69
+ db = self.ui.device_box
70
+ db.setFixedHeight(self.dimensions[0])
71
+ db.setFixedWidth(self.dimensions[1])
72
+
73
+ self.ui.step_size.setStepType(QDoubleSpinBox.AdaptiveDecimalStepType)
74
+ self.ui.stop.clicked.connect(self.on_stop)
75
+ self.ui.stop.setToolTip("Stop")
76
+ self.ui.stop.setStyleSheet(
77
+ f"QPushButton {{background-color: {get_accent_colors().emergency.name()}; color: white;}}"
78
+ )
79
+ self.ui.tweak_right.clicked.connect(self.on_tweak_right)
80
+ self.ui.tweak_right.setToolTip("Tweak right")
81
+ self.ui.tweak_left.clicked.connect(self.on_tweak_left)
82
+ self.ui.tweak_left.setToolTip("Tweak left")
83
+ self.ui.setpoint.returnPressed.connect(self.on_setpoint_change)
84
+
85
+ self.setpoint_validator = QDoubleValidator()
86
+ self.ui.setpoint.setValidator(self.setpoint_validator)
87
+ self.ui.spinner_widget.start()
88
+ self.ui.tool_button.clicked.connect(self._open_dialog_selection(self.set_positioner))
89
+ icon = material_icon(icon_name="edit_note", size=(16, 16), convert_to_pixmap=False)
90
+ self.ui.tool_button.setIcon(icon)
91
+
92
+ def force_update_readback(self):
93
+ self._init_device(self.device, self.position_update.emit, self.update_limits)
94
+
95
+ @SafeProperty(str)
96
+ def device(self):
97
+ """Property to set the device"""
98
+ return self._device
99
+
100
+ @device.setter
101
+ def device(self, value: str):
102
+ """Setter, checks if device is a string"""
103
+ if not value or not isinstance(value, str):
104
+ return
105
+ if not self._check_device_is_valid(value):
106
+ return
107
+ old_device = self._device
108
+ self._device = value
109
+ if not self.label:
110
+ self.label = value
111
+ self.device_changed.emit(old_device, value)
112
+
113
+ @SafeProperty(bool)
114
+ def hide_device_selection(self):
115
+ """Hide the device selection"""
116
+ return not self.ui.tool_button.isVisible()
117
+
118
+ @hide_device_selection.setter
119
+ def hide_device_selection(self, value: bool):
120
+ """Set the device selection visibility"""
121
+ self.ui.tool_button.setVisible(not value)
122
+
123
+ @SafeSlot(bool)
124
+ def show_device_selection(self, value: bool):
125
+ """Show the device selection
126
+
127
+ Args:
128
+ value (bool): Show the device selection
129
+ """
130
+ self.hide_device_selection = not value
131
+
132
+ @SafeSlot(str)
133
+ def set_positioner(self, positioner: str | Positioner):
134
+ """Set the device
135
+
136
+ Args:
137
+ positioner (Positioner | str) : Positioner to set, accepts str or the device
138
+ """
139
+ if isinstance(positioner, Positioner):
140
+ positioner = positioner.name
141
+ self.device = positioner
142
+
143
+ @SafeSlot(str, str)
144
+ def on_device_change(self, old_device: str, new_device: str):
145
+ """Upon changing the device, a check will be performed if the device is a Positioner.
146
+
147
+ Args:
148
+ old_device (str): The old device name.
149
+ new_device (str): The new device name.
150
+ """
151
+ if not self._check_device_is_valid(new_device):
152
+ return
153
+ self._on_device_change(
154
+ old_device,
155
+ new_device,
156
+ self.position_update.emit,
157
+ self.update_limits,
158
+ self.on_device_readback,
159
+ self._device_ui_components(new_device),
160
+ )
161
+
162
+ def _device_ui_components(self, device: str) -> DeviceUpdateUIComponents:
163
+ return {
164
+ "spinner": self.ui.spinner_widget,
165
+ "position_indicator": self.ui.position_indicator,
166
+ "readback": self.ui.readback,
167
+ "setpoint": self.ui.setpoint,
168
+ "step_size": self.ui.step_size,
169
+ "device_box": self.ui.device_box,
170
+ "stop": self.ui.stop,
171
+ "tweak_increase": self.ui.tweak_right,
172
+ "tweak_decrease": self.ui.tweak_left,
173
+ }
174
+
175
+ @SafeSlot(dict, dict)
176
+ def on_device_readback(self, msg_content: dict, metadata: dict):
177
+ """Callback for device readback.
178
+
179
+ Args:
180
+ msg_content (dict): The message content.
181
+ metadata (dict): The message metadata.
182
+ """
183
+ self._on_device_readback(
184
+ self.device,
185
+ self._device_ui_components(self.device),
186
+ msg_content,
187
+ metadata,
188
+ self.position_update.emit,
189
+ self.update_limits,
190
+ )
191
+
192
+ def update_limits(self, limits: tuple):
193
+ """Update limits
194
+
195
+ Args:
196
+ limits (tuple): Limits of the positioner
197
+ """
198
+ if limits == self._limits:
199
+ return
200
+ self._limits = limits
201
+ self._update_limits_ui(limits, self.ui.position_indicator, self.setpoint_validator)
202
+
203
+ @SafeSlot()
204
+ def on_stop(self):
205
+ self._stop_device(self.device)
206
+
207
+ @property
208
+ def step_size(self):
209
+ """Step size for tweak"""
210
+ return self.ui.step_size.value()
211
+
212
+ @SafeSlot()
213
+ def on_tweak_right(self):
214
+ """Tweak motor right"""
215
+ self.dev[self.device].move(self.step_size, relative=True)
216
+
217
+ @SafeSlot()
218
+ def on_tweak_left(self):
219
+ """Tweak motor left"""
220
+ self.dev[self.device].move(-self.step_size, relative=True)
221
+
222
+ @SafeSlot()
223
+ def on_setpoint_change(self):
224
+ """Change the setpoint for the motor"""
225
+ self.ui.setpoint.clearFocus()
226
+ setpoint = self.ui.setpoint.text()
227
+ self.dev[self.device].move(float(setpoint), relative=False)
228
+ self.ui.tweak_left.setToolTip(f"Tweak left by {self.step_size}")
229
+ self.ui.tweak_right.setToolTip(f"Tweak right by {self.step_size}")
230
+
231
+
232
+ if __name__ == "__main__": # pragma: no cover
233
+ import sys
234
+
235
+ from qtpy.QtWidgets import QApplication # pylint: disable=ungrouped-imports
236
+
237
+ app = QApplication(sys.argv)
238
+ set_theme("dark")
239
+ widget = PositionerBox(device="bpm4i")
240
+
241
+ widget.show()
242
+ sys.exit(app.exec_())
@@ -6,7 +6,7 @@ import os
6
6
  from qtpy.QtDesigner import QDesignerCustomWidgetInterface
7
7
 
8
8
  from bec_widgets.utils.bec_designer import designer_material_icon
9
- from bec_widgets.widgets.control.device_control.positioner_box.positioner_box import PositionerBox
9
+ from bec_widgets.widgets.control.device_control.positioner_box import PositionerBox
10
10
 
11
11
  DOM_XML = """
12
12
  <ui language='c++'>
@@ -6,7 +6,7 @@ def main(): # pragma: no cover
6
6
  return
7
7
  from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
8
8
 
9
- from bec_widgets.widgets.control.device_control.positioner_box.positioner_box_plugin import (
9
+ from bec_widgets.widgets.control.device_control.positioner_box.positioner_box.positioner_box_plugin import (
10
10
  PositionerBoxPlugin,
11
11
  )
12
12
 
@@ -4,36 +4,38 @@
4
4
  from qtpy.QtDesigner import QDesignerCustomWidgetInterface
5
5
 
6
6
  from bec_widgets.utils.bec_designer import designer_material_icon
7
- from bec_widgets.widgets.control.device_input.signal_combobox.signal_combobox import SignalComboBox
7
+ from bec_widgets.widgets.control.device_control.positioner_box.positioner_box_2d.positioner_box_2d import (
8
+ PositionerBox2D,
9
+ )
8
10
 
9
11
  DOM_XML = """
10
12
  <ui language='c++'>
11
- <widget class='SignalComboBox' name='signal_combo_box'>
13
+ <widget class='PositionerBox2D' name='positioner_box2_d'>
12
14
  </widget>
13
15
  </ui>
14
16
  """
15
17
 
16
18
 
17
- class SignalComboBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
19
+ class PositionerBox2DPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
18
20
  def __init__(self):
19
21
  super().__init__()
20
22
  self._form_editor = None
21
23
 
22
24
  def createWidget(self, parent):
23
- t = SignalComboBox(parent)
25
+ t = PositionerBox2D(parent)
24
26
  return t
25
27
 
26
28
  def domXml(self):
27
29
  return DOM_XML
28
30
 
29
31
  def group(self):
30
- return "BEC Input Widgets"
32
+ return "Device Control"
31
33
 
32
34
  def icon(self):
33
- return designer_material_icon(SignalComboBox.ICON_NAME)
35
+ return designer_material_icon(PositionerBox2D.ICON_NAME)
34
36
 
35
37
  def includeFile(self):
36
- return "signal_combo_box"
38
+ return "positioner_box2_d"
37
39
 
38
40
  def initialize(self, form_editor):
39
41
  self._form_editor = form_editor
@@ -45,10 +47,10 @@ class SignalComboBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
45
47
  return self._form_editor is not None
46
48
 
47
49
  def name(self):
48
- return "SignalComboBox"
50
+ return "PositionerBox2D"
49
51
 
50
52
  def toolTip(self):
51
- return ""
53
+ return "Simple Widget to control two positioners in box form"
52
54
 
53
55
  def whatsThis(self):
54
56
  return self.toolTip()