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.
- CHANGELOG.md +37 -0
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +46 -0
- bec_widgets/utils/bec_signal_proxy.py +39 -11
- bec_widgets/widgets/containers/dock/dock_area.py +1 -1
- bec_widgets/widgets/control/device_control/positioner_box/__init__.py +11 -0
- bec_widgets/widgets/control/device_control/positioner_box/_base/__init__.py +3 -0
- bec_widgets/widgets/control/device_control/positioner_box/_base/positioner_box_base.py +243 -0
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box/__init__.py +0 -0
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.py +242 -0
- bec_widgets/widgets/control/device_control/positioner_box/{positioner_box_plugin.py → positioner_box/positioner_box_plugin.py} +1 -1
- bec_widgets/widgets/control/device_control/positioner_box/{register_positioner_box.py → positioner_box/register_positioner_box.py} +1 -1
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/__init__.py +0 -0
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box2_d.pyproject +1 -0
- 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
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.py +482 -0
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.ui +562 -0
- 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
- bec_widgets/widgets/control/device_control/positioner_box/{positioner_control_line.py → positioner_control_line/positioner_control_line.py} +5 -2
- bec_widgets/widgets/control/device_control/positioner_box/{positioner_control_line_plugin.py → positioner_control_line/positioner_control_line_plugin.py} +1 -3
- bec_widgets/widgets/control/device_control/positioner_box/{register_positioner_control_line.py → positioner_control_line/register_positioner_control_line.py} +1 -1
- bec_widgets/widgets/control/device_control/positioner_group/positioner_group.py +7 -6
- {bec_widgets-1.16.5.dist-info → bec_widgets-1.17.1.dist-info}/METADATA +1 -1
- {bec_widgets-1.16.5.dist-info → bec_widgets-1.17.1.dist-info}/RECORD +32 -26
- pyproject.toml +1 -1
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box.py +0 -352
- bec_widgets/widgets/control/device_input/signal_combobox/signal_combo_box.pyproject +0 -1
- /bec_widgets/widgets/control/device_control/positioner_box/{positioner_box.pyproject → positioner_box/positioner_box.pyproject} +0 -0
- /bec_widgets/widgets/control/device_control/positioner_box/{positioner_box.ui → positioner_box/positioner_box.ui} +0 -0
- /bec_widgets/widgets/control/device_control/positioner_box/{positioner_control_line.pyproject → positioner_control_line/positioner_control_line.pyproject} +0 -0
- /bec_widgets/widgets/control/device_control/positioner_box/{positioner_control_line.ui → positioner_control_line/positioner_control_line.ui} +0 -0
- {bec_widgets-1.16.5.dist-info → bec_widgets-1.17.1.dist-info}/WHEEL +0 -0
- {bec_widgets-1.16.5.dist-info → bec_widgets-1.17.1.dist-info}/entry_points.txt +0 -0
- {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
|
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
|
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
{'files': ['positioner_box_2d.py']}
|
@@ -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.
|
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='
|
13
|
+
<widget class='PositionerBox2D' name='positioner_box2_d'>
|
12
14
|
</widget>
|
13
15
|
</ui>
|
14
16
|
"""
|
15
17
|
|
16
18
|
|
17
|
-
class
|
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 =
|
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 "
|
32
|
+
return "Device Control"
|
31
33
|
|
32
34
|
def icon(self):
|
33
|
-
return designer_material_icon(
|
35
|
+
return designer_material_icon(PositionerBox2D.ICON_NAME)
|
34
36
|
|
35
37
|
def includeFile(self):
|
36
|
-
return "
|
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 "
|
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()
|