bec-widgets 0.54.0__py3-none-any.whl → 0.56.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.
- .gitlab-ci.yml +113 -8
- CHANGELOG.md +32 -21
- PKG-INFO +3 -1
- bec_widgets/cli/client.py +252 -0
- bec_widgets/cli/generate_cli.py +4 -1
- bec_widgets/cli/rpc_wigdet_handler.py +2 -1
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +29 -37
- bec_widgets/examples/motor_movement/motor_control_compilations.py +1 -7
- bec_widgets/utils/__init__.py +1 -0
- bec_widgets/utils/crosshair.py +13 -9
- bec_widgets/utils/ui_loader.py +58 -0
- bec_widgets/widgets/__init__.py +1 -0
- bec_widgets/widgets/motor_control/motor_table/motor_table.py +44 -43
- bec_widgets/widgets/motor_control/movement_absolute/movement_absolute.py +25 -23
- bec_widgets/widgets/motor_control/movement_relative/movement_relative.py +51 -48
- bec_widgets/widgets/spiral_progress_bar/__init__.py +1 -0
- bec_widgets/widgets/spiral_progress_bar/ring.py +184 -0
- bec_widgets/widgets/spiral_progress_bar/spiral_progress_bar.py +594 -0
- {bec_widgets-0.54.0.dist-info → bec_widgets-0.56.0.dist-info}/METADATA +3 -1
- {bec_widgets-0.54.0.dist-info → bec_widgets-0.56.0.dist-info}/RECORD +29 -46
- docs/user/apps.md +1 -26
- pyproject.toml +2 -1
- tests/end-2-end/test_bec_dock_rpc_e2e.py +81 -0
- tests/unit_tests/test_client_utils.py +2 -2
- tests/unit_tests/test_crosshair.py +5 -5
- tests/unit_tests/test_motor_control.py +49 -45
- tests/unit_tests/test_spiral_progress_bar.py +338 -0
- bec_widgets/examples/eiger_plot/__init__.py +0 -0
- bec_widgets/examples/eiger_plot/eiger_plot.py +0 -307
- bec_widgets/examples/eiger_plot/eiger_plot.ui +0 -207
- bec_widgets/examples/mca_readout/__init__.py +0 -0
- bec_widgets/examples/mca_readout/mca_plot.py +0 -159
- bec_widgets/examples/mca_readout/mca_sim.py +0 -28
- bec_widgets/examples/modular_app/___init__.py +0 -0
- bec_widgets/examples/modular_app/modular.ui +0 -92
- bec_widgets/examples/modular_app/modular_app.py +0 -197
- bec_widgets/examples/motor_movement/config_example.yaml +0 -17
- bec_widgets/examples/motor_movement/csax_bec_config.yaml +0 -10
- bec_widgets/examples/motor_movement/csaxs_config.yaml +0 -17
- bec_widgets/examples/motor_movement/motor_example.py +0 -1344
- bec_widgets/examples/stream_plot/__init__.py +0 -0
- bec_widgets/examples/stream_plot/line_plot.ui +0 -155
- bec_widgets/examples/stream_plot/stream_plot.py +0 -337
- docs/user/apps/modular_app.md +0 -6
- docs/user/apps/motor_app.md +0 -34
- docs/user/apps/motor_app_10fps.gif +0 -0
- docs/user/apps/plot_app.md +0 -6
- tests/unit_tests/test_eiger_plot.py +0 -115
- tests/unit_tests/test_stream_plot.py +0 -158
- {bec_widgets-0.54.0.dist-info → bec_widgets-0.56.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.54.0.dist-info → bec_widgets-0.56.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,8 +3,10 @@ import os
|
|
3
3
|
from qtpy import uic
|
4
4
|
from qtpy.QtCore import Signal as pyqtSignal
|
5
5
|
from qtpy.QtCore import Slot as pyqtSlot
|
6
|
+
from qtpy.QtWidgets import QWidget
|
6
7
|
|
7
|
-
from bec_widgets.
|
8
|
+
from bec_widgets.utils import UILoader
|
9
|
+
from bec_widgets.widgets.motor_control.motor_control import MotorControlErrors, MotorControlWidget
|
8
10
|
|
9
11
|
|
10
12
|
class MotorControlAbsolute(MotorControlWidget):
|
@@ -23,26 +25,26 @@ class MotorControlAbsolute(MotorControlWidget):
|
|
23
25
|
def _load_ui(self):
|
24
26
|
"""Load the UI from the .ui file."""
|
25
27
|
current_path = os.path.dirname(__file__)
|
26
|
-
|
28
|
+
self.ui = UILoader().load_ui(os.path.join(current_path, "movement_absolute.ui"), self)
|
27
29
|
|
28
30
|
def _init_ui(self):
|
29
31
|
"""Initialize the UI."""
|
30
32
|
|
31
33
|
# Check if there are any motors connected
|
32
34
|
if self.motor_x is None or self.motor_y is None:
|
33
|
-
self.motorControl_absolute.setEnabled(False)
|
35
|
+
self.ui.motorControl_absolute.setEnabled(False)
|
34
36
|
return
|
35
37
|
|
36
38
|
# Move to absolute coordinates
|
37
|
-
self.pushButton_go_absolute.clicked.connect(
|
39
|
+
self.ui.pushButton_go_absolute.clicked.connect(
|
38
40
|
lambda: self.move_motor_absolute(
|
39
|
-
self.spinBox_absolute_x.value(), self.spinBox_absolute_y.value()
|
41
|
+
self.ui.spinBox_absolute_x.value(), self.ui.spinBox_absolute_y.value()
|
40
42
|
)
|
41
43
|
)
|
42
44
|
|
43
|
-
self.pushButton_set.clicked.connect(self.save_absolute_coordinates)
|
44
|
-
self.pushButton_save.clicked.connect(self.save_current_coordinates)
|
45
|
-
self.pushButton_stop.clicked.connect(self.motor_thread.stop_movement)
|
45
|
+
self.ui.pushButton_set.clicked.connect(self.save_absolute_coordinates)
|
46
|
+
self.ui.pushButton_save.clicked.connect(self.save_current_coordinates)
|
47
|
+
self.ui.pushButton_stop.clicked.connect(self.motor_thread.stop_movement)
|
46
48
|
|
47
49
|
# Enable/Disable GUI
|
48
50
|
self.motor_thread.lock_gui.connect(self.enable_motor_controls)
|
@@ -80,11 +82,11 @@ class MotorControlAbsolute(MotorControlWidget):
|
|
80
82
|
"""
|
81
83
|
|
82
84
|
# Disable or enable all controls within the motorControl_absolute group box
|
83
|
-
for widget in self.motorControl_absolute.findChildren(QWidget):
|
85
|
+
for widget in self.ui.motorControl_absolute.findChildren(QWidget):
|
84
86
|
widget.setEnabled(enable)
|
85
87
|
|
86
88
|
# Enable the pushButton_stop if the motor is moving
|
87
|
-
self.pushButton_stop.setEnabled(True)
|
89
|
+
self.ui.pushButton_stop.setEnabled(True)
|
88
90
|
|
89
91
|
@pyqtSlot(str, str)
|
90
92
|
def change_motors(self, motor_x: str, motor_y: str):
|
@@ -109,8 +111,8 @@ class MotorControlAbsolute(MotorControlWidget):
|
|
109
111
|
"""
|
110
112
|
self.precision = precision
|
111
113
|
self.config["motor_control"]["precision"] = precision
|
112
|
-
self.spinBox_absolute_x.setDecimals(precision)
|
113
|
-
self.spinBox_absolute_y.setDecimals(precision)
|
114
|
+
self.ui.spinBox_absolute_x.setDecimals(precision)
|
115
|
+
self.ui.spinBox_absolute_y.setDecimals(precision)
|
114
116
|
|
115
117
|
def move_motor_absolute(self, x: float, y: float) -> None:
|
116
118
|
"""
|
@@ -122,32 +124,32 @@ class MotorControlAbsolute(MotorControlWidget):
|
|
122
124
|
# self._enable_motor_controls(False)
|
123
125
|
target_coordinates = (x, y)
|
124
126
|
self.motor_thread.move_absolute(self.motor_x, self.motor_y, target_coordinates)
|
125
|
-
if self.checkBox_save_with_go.isChecked():
|
127
|
+
if self.ui.checkBox_save_with_go.isChecked():
|
126
128
|
self.save_absolute_coordinates()
|
127
129
|
|
128
130
|
def _init_keyboard_shortcuts(self):
|
129
131
|
"""Initialize the keyboard shortcuts."""
|
130
132
|
# Go absolute button
|
131
|
-
self.pushButton_go_absolute.setShortcut("Ctrl+G")
|
132
|
-
self.pushButton_go_absolute.setToolTip("Ctrl+G")
|
133
|
+
self.ui.pushButton_go_absolute.setShortcut("Ctrl+G")
|
134
|
+
self.ui.pushButton_go_absolute.setToolTip("Ctrl+G")
|
133
135
|
|
134
136
|
# Set absolute coordinates
|
135
|
-
self.pushButton_set.setShortcut("Ctrl+D")
|
136
|
-
self.pushButton_set.setToolTip("Ctrl+D")
|
137
|
+
self.ui.pushButton_set.setShortcut("Ctrl+D")
|
138
|
+
self.ui.pushButton_set.setToolTip("Ctrl+D")
|
137
139
|
|
138
140
|
# Save Current coordinates
|
139
|
-
self.pushButton_save.setShortcut("Ctrl+S")
|
140
|
-
self.pushButton_save.setToolTip("Ctrl+S")
|
141
|
+
self.ui.pushButton_save.setShortcut("Ctrl+S")
|
142
|
+
self.ui.pushButton_save.setToolTip("Ctrl+S")
|
141
143
|
|
142
144
|
# Stop Button
|
143
|
-
self.pushButton_stop.setShortcut("Ctrl+X")
|
144
|
-
self.pushButton_stop.setToolTip("Ctrl+X")
|
145
|
+
self.ui.pushButton_stop.setShortcut("Ctrl+X")
|
146
|
+
self.ui.pushButton_stop.setToolTip("Ctrl+X")
|
145
147
|
|
146
148
|
def save_absolute_coordinates(self):
|
147
149
|
"""Emit the setup coordinates from the spinboxes"""
|
148
150
|
|
149
|
-
x, y = round(self.spinBox_absolute_x.value(), self.precision), round(
|
150
|
-
self.spinBox_absolute_y.value(), self.precision
|
151
|
+
x, y = round(self.ui.spinBox_absolute_x.value(), self.precision), round(
|
152
|
+
self.ui.spinBox_absolute_y.value(), self.precision
|
151
153
|
)
|
152
154
|
self.coordinates_signal.emit((x, y))
|
153
155
|
|
@@ -7,6 +7,7 @@ from qtpy.QtCore import Slot as pyqtSlot
|
|
7
7
|
from qtpy.QtGui import QKeySequence
|
8
8
|
from qtpy.QtWidgets import QDoubleSpinBox, QShortcut, QWidget
|
9
9
|
|
10
|
+
from bec_widgets.utils import UILoader
|
10
11
|
from bec_widgets.widgets.motor_control.motor_control import MotorControlWidget
|
11
12
|
|
12
13
|
|
@@ -27,7 +28,7 @@ class MotorControlRelative(MotorControlWidget):
|
|
27
28
|
"""Load the UI from the .ui file."""
|
28
29
|
# Loading UI
|
29
30
|
current_path = os.path.dirname(__file__)
|
30
|
-
|
31
|
+
self.ui = UILoader().load_ui(os.path.join(current_path, "movement_relative.ui"), self)
|
31
32
|
|
32
33
|
def _init_ui(self):
|
33
34
|
"""Initialize the UI."""
|
@@ -51,15 +52,15 @@ class MotorControlRelative(MotorControlWidget):
|
|
51
52
|
|
52
53
|
# Update step precision
|
53
54
|
self.precision = self.config["motor_control"]["precision"]
|
54
|
-
self.spinBox_precision.setValue(self.precision)
|
55
|
+
self.ui.spinBox_precision.setValue(self.precision)
|
55
56
|
|
56
57
|
# Update step sizes
|
57
|
-
self.spinBox_step_x.setValue(self.config["motor_control"]["step_size_x"])
|
58
|
-
self.spinBox_step_y.setValue(self.config["motor_control"]["step_size_y"])
|
58
|
+
self.ui.spinBox_step_x.setValue(self.config["motor_control"]["step_size_x"])
|
59
|
+
self.ui.spinBox_step_y.setValue(self.config["motor_control"]["step_size_y"])
|
59
60
|
|
60
61
|
# Checkboxes for keyboard shortcuts and x/y step size link
|
61
|
-
self.checkBox_same_xy.setChecked(self.config["motor_control"]["step_x_y_same"])
|
62
|
-
self.checkBox_enableArrows.setChecked(self.config["motor_control"]["move_with_arrows"])
|
62
|
+
self.ui.checkBox_same_xy.setChecked(self.config["motor_control"]["step_x_y_same"])
|
63
|
+
self.ui.checkBox_enableArrows.setChecked(self.config["motor_control"]["move_with_arrows"])
|
63
64
|
|
64
65
|
self._init_ui()
|
65
66
|
|
@@ -67,30 +68,32 @@ class MotorControlRelative(MotorControlWidget):
|
|
67
68
|
"""Initialize the motor control elements"""
|
68
69
|
|
69
70
|
# Connect checkbox and spinBoxes
|
70
|
-
self.checkBox_same_xy.stateChanged.connect(self._sync_step_sizes)
|
71
|
-
self.spinBox_step_x.valueChanged.connect(self._update_step_size_x)
|
72
|
-
self.spinBox_step_y.valueChanged.connect(self._update_step_size_y)
|
71
|
+
self.ui.checkBox_same_xy.stateChanged.connect(self._sync_step_sizes)
|
72
|
+
self.ui.spinBox_step_x.valueChanged.connect(self._update_step_size_x)
|
73
|
+
self.ui.spinBox_step_y.valueChanged.connect(self._update_step_size_y)
|
73
74
|
|
74
|
-
self.toolButton_right.clicked.connect(
|
75
|
+
self.ui.toolButton_right.clicked.connect(
|
75
76
|
lambda: self.move_motor_relative(self.motor_x, "x", 1)
|
76
77
|
)
|
77
|
-
self.toolButton_left.clicked.connect(
|
78
|
+
self.ui.toolButton_left.clicked.connect(
|
78
79
|
lambda: self.move_motor_relative(self.motor_x, "x", -1)
|
79
80
|
)
|
80
|
-
self.toolButton_up.clicked.connect(
|
81
|
-
|
81
|
+
self.ui.toolButton_up.clicked.connect(
|
82
|
+
lambda: self.move_motor_relative(self.motor_y, "y", 1)
|
83
|
+
)
|
84
|
+
self.ui.toolButton_down.clicked.connect(
|
82
85
|
lambda: self.move_motor_relative(self.motor_y, "y", -1)
|
83
86
|
)
|
84
87
|
|
85
88
|
# Switch between key shortcuts active
|
86
|
-
self.checkBox_enableArrows.stateChanged.connect(self._update_arrow_key_shortcuts)
|
89
|
+
self.ui.checkBox_enableArrows.stateChanged.connect(self._update_arrow_key_shortcuts)
|
87
90
|
self._update_arrow_key_shortcuts()
|
88
91
|
|
89
92
|
# Enable/Disable GUI
|
90
93
|
self.motor_thread.lock_gui.connect(self.enable_motor_controls)
|
91
94
|
|
92
95
|
# Precision update
|
93
|
-
self.spinBox_precision.valueChanged.connect(lambda x: self._update_precision(x))
|
96
|
+
self.ui.spinBox_precision.valueChanged.connect(lambda x: self._update_precision(x))
|
94
97
|
|
95
98
|
# Error messages
|
96
99
|
self.motor_thread.motor_error.connect(
|
@@ -98,7 +101,7 @@ class MotorControlRelative(MotorControlWidget):
|
|
98
101
|
)
|
99
102
|
|
100
103
|
# Stop Button
|
101
|
-
self.pushButton_stop.clicked.connect(self.motor_thread.stop_movement)
|
104
|
+
self.ui.pushButton_stop.clicked.connect(self.motor_thread.stop_movement)
|
102
105
|
|
103
106
|
def _init_keyboard_shortcuts(self) -> None:
|
104
107
|
"""Initialize the keyboard shortcuts"""
|
@@ -107,42 +110,42 @@ class MotorControlRelative(MotorControlWidget):
|
|
107
110
|
increase_x_shortcut = QShortcut(QKeySequence("Ctrl+A"), self)
|
108
111
|
decrease_x_shortcut = QShortcut(QKeySequence("Ctrl+Z"), self)
|
109
112
|
increase_x_shortcut.activated.connect(
|
110
|
-
lambda: self._change_step_size(self.spinBox_step_x, 2)
|
113
|
+
lambda: self._change_step_size(self.ui.spinBox_step_x, 2)
|
111
114
|
)
|
112
115
|
decrease_x_shortcut.activated.connect(
|
113
|
-
lambda: self._change_step_size(self.spinBox_step_x, 0.5)
|
116
|
+
lambda: self._change_step_size(self.ui.spinBox_step_x, 0.5)
|
114
117
|
)
|
115
|
-
self.spinBox_step_x.setToolTip("Increase step size: Ctrl+A\nDecrease step size: Ctrl+Z")
|
118
|
+
self.ui.spinBox_step_x.setToolTip("Increase step size: Ctrl+A\nDecrease step size: Ctrl+Z")
|
116
119
|
|
117
120
|
# Increase/decrease step size for Y motor
|
118
121
|
increase_y_shortcut = QShortcut(QKeySequence("Alt+A"), self)
|
119
122
|
decrease_y_shortcut = QShortcut(QKeySequence("Alt+Z"), self)
|
120
123
|
increase_y_shortcut.activated.connect(
|
121
|
-
lambda: self._change_step_size(self.spinBox_step_y, 2)
|
124
|
+
lambda: self._change_step_size(self.ui.spinBox_step_y, 2)
|
122
125
|
)
|
123
126
|
decrease_y_shortcut.activated.connect(
|
124
|
-
lambda: self._change_step_size(self.spinBox_step_y, 0.5)
|
127
|
+
lambda: self._change_step_size(self.ui.spinBox_step_y, 0.5)
|
125
128
|
)
|
126
|
-
self.spinBox_step_y.setToolTip("Increase step size: Alt+A\nDecrease step size: Alt+Z")
|
129
|
+
self.ui.spinBox_step_y.setToolTip("Increase step size: Alt+A\nDecrease step size: Alt+Z")
|
127
130
|
|
128
131
|
# Stop Button
|
129
|
-
self.pushButton_stop.setShortcut("Ctrl+X")
|
130
|
-
self.pushButton_stop.setToolTip("Ctrl+X")
|
132
|
+
self.ui.pushButton_stop.setShortcut("Ctrl+X")
|
133
|
+
self.ui.pushButton_stop.setToolTip("Ctrl+X")
|
131
134
|
|
132
135
|
def _update_arrow_key_shortcuts(self) -> None:
|
133
136
|
"""Update the arrow key shortcuts based on the checkbox state."""
|
134
|
-
if self.checkBox_enableArrows.isChecked():
|
137
|
+
if self.ui.checkBox_enableArrows.isChecked():
|
135
138
|
# Set the arrow key shortcuts for motor movement
|
136
|
-
self.toolButton_right.setShortcut(Qt.Key_Right)
|
137
|
-
self.toolButton_left.setShortcut(Qt.Key_Left)
|
138
|
-
self.toolButton_up.setShortcut(Qt.Key_Up)
|
139
|
-
self.toolButton_down.setShortcut(Qt.Key_Down)
|
139
|
+
self.ui.toolButton_right.setShortcut(Qt.Key_Right)
|
140
|
+
self.ui.toolButton_left.setShortcut(Qt.Key_Left)
|
141
|
+
self.ui.toolButton_up.setShortcut(Qt.Key_Up)
|
142
|
+
self.ui.toolButton_down.setShortcut(Qt.Key_Down)
|
140
143
|
else:
|
141
144
|
# Clear the shortcuts
|
142
|
-
self.toolButton_right.setShortcut("")
|
143
|
-
self.toolButton_left.setShortcut("")
|
144
|
-
self.toolButton_up.setShortcut("")
|
145
|
-
self.toolButton_down.setShortcut("")
|
145
|
+
self.ui.toolButton_right.setShortcut("")
|
146
|
+
self.ui.toolButton_left.setShortcut("")
|
147
|
+
self.ui.toolButton_up.setShortcut("")
|
148
|
+
self.ui.toolButton_down.setShortcut("")
|
146
149
|
|
147
150
|
def _update_precision(self, precision: int) -> None:
|
148
151
|
"""
|
@@ -150,8 +153,8 @@ class MotorControlRelative(MotorControlWidget):
|
|
150
153
|
Args:
|
151
154
|
precision(int): Precision of the coordinates.
|
152
155
|
"""
|
153
|
-
self.spinBox_step_x.setDecimals(precision)
|
154
|
-
self.spinBox_step_y.setDecimals(precision)
|
156
|
+
self.ui.spinBox_step_x.setDecimals(precision)
|
157
|
+
self.ui.spinBox_step_y.setDecimals(precision)
|
155
158
|
self.precision_signal.emit(precision)
|
156
159
|
|
157
160
|
def _change_step_size(self, spinBox: QDoubleSpinBox, factor: float) -> None:
|
@@ -167,21 +170,21 @@ class MotorControlRelative(MotorControlWidget):
|
|
167
170
|
|
168
171
|
def _sync_step_sizes(self):
|
169
172
|
"""Sync step sizes based on checkbox state."""
|
170
|
-
if self.checkBox_same_xy.isChecked():
|
171
|
-
value = self.spinBox_step_x.value()
|
172
|
-
self.spinBox_step_y.setValue(value)
|
173
|
+
if self.ui.checkBox_same_xy.isChecked():
|
174
|
+
value = self.ui.spinBox_step_x.value()
|
175
|
+
self.ui.spinBox_step_y.setValue(value)
|
173
176
|
|
174
177
|
def _update_step_size_x(self):
|
175
178
|
"""Update step size for x if checkbox is checked."""
|
176
|
-
if self.checkBox_same_xy.isChecked():
|
177
|
-
value = self.spinBox_step_x.value()
|
178
|
-
self.spinBox_step_y.setValue(value)
|
179
|
+
if self.ui.checkBox_same_xy.isChecked():
|
180
|
+
value = self.ui.spinBox_step_x.value()
|
181
|
+
self.ui.spinBox_step_y.setValue(value)
|
179
182
|
|
180
183
|
def _update_step_size_y(self):
|
181
184
|
"""Update step size for y if checkbox is checked."""
|
182
|
-
if self.checkBox_same_xy.isChecked():
|
183
|
-
value = self.spinBox_step_y.value()
|
184
|
-
self.spinBox_step_x.setValue(value)
|
185
|
+
if self.ui.checkBox_same_xy.isChecked():
|
186
|
+
value = self.ui.spinBox_step_y.value()
|
187
|
+
self.ui.spinBox_step_x.setValue(value)
|
185
188
|
|
186
189
|
@pyqtSlot(str, str)
|
187
190
|
def change_motors(self, motor_x: str, motor_y: str):
|
@@ -206,11 +209,11 @@ class MotorControlRelative(MotorControlWidget):
|
|
206
209
|
"""
|
207
210
|
|
208
211
|
# Disable or enable all controls within the motorControl_absolute group box
|
209
|
-
for widget in self.motorControl.findChildren(QWidget):
|
212
|
+
for widget in self.ui.motorControl.findChildren(QWidget):
|
210
213
|
widget.setEnabled(disable)
|
211
214
|
|
212
215
|
# Enable the pushButton_stop if the motor is moving
|
213
|
-
self.pushButton_stop.setEnabled(True)
|
216
|
+
self.ui.pushButton_stop.setEnabled(True)
|
214
217
|
|
215
218
|
def move_motor_relative(self, motor, axis: str, direction: int) -> None:
|
216
219
|
"""
|
@@ -221,7 +224,7 @@ class MotorControlRelative(MotorControlWidget):
|
|
221
224
|
direction(int): Direction to move. 1 for positive, -1 for negative.
|
222
225
|
"""
|
223
226
|
if axis == "x":
|
224
|
-
step = direction * self.spinBox_step_x.value()
|
227
|
+
step = direction * self.ui.spinBox_step_x.value()
|
225
228
|
elif axis == "y":
|
226
|
-
step = direction * self.spinBox_step_y.value()
|
229
|
+
step = direction * self.ui.spinBox_step_y.value()
|
227
230
|
self.motor_thread.move_relative(motor, step)
|
@@ -0,0 +1 @@
|
|
1
|
+
from .spiral_progress_bar import SpiralProgressBar
|
@@ -0,0 +1,184 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Literal, Optional
|
4
|
+
|
5
|
+
from bec_lib.endpoints import EndpointInfo
|
6
|
+
from pydantic import BaseModel, Field, field_validator
|
7
|
+
from pydantic_core import PydanticCustomError
|
8
|
+
from qtpy import QtGui
|
9
|
+
|
10
|
+
from bec_widgets.utils import BECConnector, ConnectionConfig
|
11
|
+
|
12
|
+
|
13
|
+
class RingConnections(BaseModel):
|
14
|
+
slot: Literal["on_scan_progress", "on_device_readback"] = None
|
15
|
+
endpoint: EndpointInfo | str = None
|
16
|
+
|
17
|
+
@field_validator("endpoint")
|
18
|
+
def validate_endpoint(cls, v, values):
|
19
|
+
slot = values.data["slot"]
|
20
|
+
endpoint = v.endpoint if isinstance(v, EndpointInfo) else v
|
21
|
+
if slot == "on_scan_progress":
|
22
|
+
if endpoint != "scans/scan_progress":
|
23
|
+
raise PydanticCustomError(
|
24
|
+
"unsupported endpoint",
|
25
|
+
"For slot 'on_scan_progress', endpoint must be MessageEndpoint.scan_progress or 'scans/scan_progress'.",
|
26
|
+
{"wrong_value": v},
|
27
|
+
)
|
28
|
+
elif slot == "on_device_readback":
|
29
|
+
if not endpoint.startswith("internal/devices/readback/"):
|
30
|
+
raise PydanticCustomError(
|
31
|
+
"unsupported endpoint",
|
32
|
+
"For slot 'on_device_readback', endpoint must be MessageEndpoint.device_readback(device) or 'internal/devices/readback/{device}'.",
|
33
|
+
{"wrong_value": v},
|
34
|
+
)
|
35
|
+
return v
|
36
|
+
|
37
|
+
|
38
|
+
class RingConfig(ConnectionConfig):
|
39
|
+
direction: int | None = Field(
|
40
|
+
-1, description="Direction of the progress bars. -1 for clockwise, 1 for counter-clockwise."
|
41
|
+
)
|
42
|
+
color: str | tuple | None = Field(
|
43
|
+
(0, 159, 227, 255),
|
44
|
+
description="Color for the progress bars. Can be tuple (R, G, B, A) or string HEX Code.",
|
45
|
+
)
|
46
|
+
background_color: str | tuple | None = Field(
|
47
|
+
(200, 200, 200, 50),
|
48
|
+
description="Background color for the progress bars. Can be tuple (R, G, B, A) or string HEX Code.",
|
49
|
+
)
|
50
|
+
index: int | None = Field(0, description="Index of the progress bar. 0 is outer ring.")
|
51
|
+
line_width: int | None = Field(5, description="Line widths for the progress bars.")
|
52
|
+
start_position: int | None = Field(
|
53
|
+
90,
|
54
|
+
description="Start position for the progress bars in degrees. Default is 90 degrees - corespons to "
|
55
|
+
"the top of the ring.",
|
56
|
+
)
|
57
|
+
min_value: int | None = Field(0, description="Minimum value for the progress bars.")
|
58
|
+
max_value: int | None = Field(100, description="Maximum value for the progress bars.")
|
59
|
+
precision: int | None = Field(3, description="Precision for the progress bars.")
|
60
|
+
update_behaviour: Literal["manual", "auto"] | None = Field(
|
61
|
+
"auto", description="Update behaviour for the progress bars."
|
62
|
+
)
|
63
|
+
connections: RingConnections | None = Field(
|
64
|
+
default_factory=RingConnections, description="Connections for the progress bars."
|
65
|
+
)
|
66
|
+
|
67
|
+
|
68
|
+
class Ring(BECConnector):
|
69
|
+
USER_ACCESS = [
|
70
|
+
"get_all_rpc",
|
71
|
+
"rpc_id",
|
72
|
+
"config_dict",
|
73
|
+
"set_value",
|
74
|
+
"set_color",
|
75
|
+
"set_background",
|
76
|
+
"set_line_width",
|
77
|
+
"set_min_max_values",
|
78
|
+
"set_start_angle",
|
79
|
+
"set_connections",
|
80
|
+
"reset_connection",
|
81
|
+
]
|
82
|
+
|
83
|
+
def __init__(
|
84
|
+
self,
|
85
|
+
parent=None,
|
86
|
+
parent_progress_widget=None,
|
87
|
+
config: RingConfig | dict | None = None,
|
88
|
+
client=None,
|
89
|
+
gui_id: Optional[str] = None,
|
90
|
+
):
|
91
|
+
if config is None:
|
92
|
+
config = RingConfig(widget_class=self.__class__.__name__)
|
93
|
+
self.config = config
|
94
|
+
else:
|
95
|
+
if isinstance(config, dict):
|
96
|
+
config = RingConfig(**config)
|
97
|
+
self.config = config
|
98
|
+
super().__init__(client=client, config=config, gui_id=gui_id)
|
99
|
+
|
100
|
+
self.parent_progress_widget = parent_progress_widget
|
101
|
+
self.color = None
|
102
|
+
self.background_color = None
|
103
|
+
self.start_position = None
|
104
|
+
self.config = config
|
105
|
+
self.value = 0
|
106
|
+
self.RID = None
|
107
|
+
self._init_config_params()
|
108
|
+
|
109
|
+
def _init_config_params(self):
|
110
|
+
self.color = self.convert_color(self.config.color)
|
111
|
+
self.background_color = self.convert_color(self.config.background_color)
|
112
|
+
self.set_start_angle(self.config.start_position)
|
113
|
+
if self.config.connections:
|
114
|
+
self.set_connections(self.config.connections.slot, self.config.connections.endpoint)
|
115
|
+
|
116
|
+
def set_value(self, value: int | float):
|
117
|
+
self.value = round(
|
118
|
+
max(self.config.min_value, min(self.config.max_value, value)), self.config.precision
|
119
|
+
)
|
120
|
+
|
121
|
+
def set_color(self, color: str | tuple):
|
122
|
+
self.config.color = color
|
123
|
+
self.color = self.convert_color(color)
|
124
|
+
|
125
|
+
def set_background(self, color: str | tuple):
|
126
|
+
self.config.background_color = color
|
127
|
+
self.color = self.convert_color(color)
|
128
|
+
|
129
|
+
def set_line_width(self, width: int):
|
130
|
+
self.config.line_width = width
|
131
|
+
|
132
|
+
def set_min_max_values(self, min_value: int, max_value: int):
|
133
|
+
self.config.min_value = min_value
|
134
|
+
self.config.max_value = max_value
|
135
|
+
|
136
|
+
def set_start_angle(self, start_angle: int):
|
137
|
+
self.config.start_position = start_angle
|
138
|
+
self.start_position = start_angle * 16
|
139
|
+
|
140
|
+
@staticmethod
|
141
|
+
def convert_color(color):
|
142
|
+
converted_color = None
|
143
|
+
if isinstance(color, str):
|
144
|
+
converted_color = QtGui.QColor(color)
|
145
|
+
elif isinstance(color, tuple):
|
146
|
+
converted_color = QtGui.QColor(*color)
|
147
|
+
return converted_color
|
148
|
+
|
149
|
+
def set_connections(self, slot: str, endpoint: str | EndpointInfo):
|
150
|
+
if self.config.connections.endpoint == endpoint and self.config.connections.slot == slot:
|
151
|
+
return
|
152
|
+
else:
|
153
|
+
self.bec_dispatcher.disconnect_slot(
|
154
|
+
self.config.connections.slot, self.config.connections.endpoint
|
155
|
+
)
|
156
|
+
self.config.connections = RingConnections(slot=slot, endpoint=endpoint)
|
157
|
+
self.bec_dispatcher.connect_slot(getattr(self, slot), endpoint)
|
158
|
+
|
159
|
+
def reset_connection(self):
|
160
|
+
self.bec_dispatcher.disconnect_slot(
|
161
|
+
self.config.connections.slot, self.config.connections.endpoint
|
162
|
+
)
|
163
|
+
self.config.connections = RingConnections()
|
164
|
+
|
165
|
+
def on_scan_progress(self, msg, meta):
|
166
|
+
current_RID = meta.get("RID", None)
|
167
|
+
if current_RID != self.RID:
|
168
|
+
self.set_min_max_values(0, msg.get("max_value", 100))
|
169
|
+
self.set_value(msg.get("value", 0))
|
170
|
+
self.parent_progress_widget.update()
|
171
|
+
|
172
|
+
def on_device_readback(self, msg, meta):
|
173
|
+
if isinstance(self.config.connections.endpoint, EndpointInfo):
|
174
|
+
endpoint = self.config.connections.endpoint.endpoint
|
175
|
+
else:
|
176
|
+
endpoint = self.config.connections.endpoint
|
177
|
+
device = endpoint.split("/")[-1]
|
178
|
+
value = msg.get("signals").get(device).get("value")
|
179
|
+
self.set_value(value)
|
180
|
+
self.parent_progress_widget.update()
|
181
|
+
|
182
|
+
def cleanup(self):
|
183
|
+
self.reset_connection()
|
184
|
+
super().cleanup()
|