bec-widgets 0.44.5__py3-none-any.whl → 0.45.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.
- bec_widgets/cli/client.py +100 -1
- bec_widgets/cli/generate_cli.py +2 -1
- bec_widgets/widgets/__init__.py +1 -1
- bec_widgets/widgets/figure/figure.py +72 -1
- bec_widgets/widgets/plots/__init__.py +1 -0
- bec_widgets/widgets/plots/motor_map.py +423 -0
- bec_widgets/widgets/plots/plot_base.py +1 -1
- bec_widgets/widgets/plots/waveform1d.py +3 -2
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.45.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.45.0.dist-info}/RECORD +21 -19
- tests/client_mocks.py +76 -31
- tests/test_bec_figure.py +12 -1
- tests/test_bec_monitor.py +2 -66
- tests/test_bec_motor_map.py +125 -0
- tests/test_config_dialog.py +2 -63
- tests/test_motor_control.py +17 -83
- tests/test_motor_map.py +9 -66
- tests/test_waveform1d.py +26 -8
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.45.0.dist-info}/LICENSE +0 -0
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.45.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.45.0.dist-info}/top_level.txt +0 -0
tests/test_motor_control.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
from unittest.mock import MagicMock, patch
|
3
3
|
|
4
4
|
import pytest
|
5
|
-
from bec_lib.
|
5
|
+
from bec_lib.devicemanager import DeviceContainer
|
6
6
|
|
7
7
|
from bec_widgets.examples import (
|
8
8
|
MotorControlApp,
|
@@ -20,6 +20,8 @@ from bec_widgets.widgets import (
|
|
20
20
|
)
|
21
21
|
from bec_widgets.widgets.motor_control.motor_control import MotorActions
|
22
22
|
|
23
|
+
from .client_mocks import mocked_client
|
24
|
+
|
23
25
|
CONFIG_DEFAULT = {
|
24
26
|
"motor_control": {
|
25
27
|
"motor_x": "samx",
|
@@ -52,81 +54,6 @@ CONFIG_DEFAULT = {
|
|
52
54
|
],
|
53
55
|
}
|
54
56
|
|
55
|
-
#######################################################
|
56
|
-
# Client and devices fixture
|
57
|
-
#######################################################
|
58
|
-
|
59
|
-
|
60
|
-
class FakeDevice:
|
61
|
-
"""Fake minimal positioner class for testing."""
|
62
|
-
|
63
|
-
def __init__(self, name, enabled=True, limits=None, read_value=1.0):
|
64
|
-
super().__init__()
|
65
|
-
self.name = name
|
66
|
-
self.enabled = enabled
|
67
|
-
self.read_value = read_value
|
68
|
-
self.limits = limits or (-100, 100) # Default limits if not provided
|
69
|
-
|
70
|
-
def read(self):
|
71
|
-
"""Simulates reading the current position of the device."""
|
72
|
-
return {self.name: {"value": self.read_value}}
|
73
|
-
|
74
|
-
def move(self, value, relative=False):
|
75
|
-
"""Simulates moving the device to a new position."""
|
76
|
-
if relative:
|
77
|
-
self.read_value += value
|
78
|
-
else:
|
79
|
-
self.read_value = value
|
80
|
-
# Respect the limits
|
81
|
-
self.read_value = max(min(self.read_value, self.limits[1]), self.limits[0])
|
82
|
-
|
83
|
-
@property
|
84
|
-
def readback(self):
|
85
|
-
return MagicMock(get=MagicMock(return_value=self.read_value))
|
86
|
-
|
87
|
-
def describe(self):
|
88
|
-
"""Describes the device."""
|
89
|
-
return {self.name: {"source": self.name, "dtype": "number", "shape": []}}
|
90
|
-
|
91
|
-
|
92
|
-
@pytest.fixture
|
93
|
-
def mocked_client():
|
94
|
-
client = MagicMock()
|
95
|
-
|
96
|
-
# Setup the fake devices
|
97
|
-
motors = {
|
98
|
-
"samx": FakeDevice("samx", limits=[-10, 10], read_value=2.0),
|
99
|
-
"samy": FakeDevice("samy", limits=[-5, 5], read_value=3.0),
|
100
|
-
"aptrx": FakeDevice("aptrx", read_value=4.0),
|
101
|
-
"aptry": FakeDevice("aptry", read_value=5.0),
|
102
|
-
}
|
103
|
-
|
104
|
-
client.device_manager.devices = MagicMock()
|
105
|
-
client.device_manager.devices.__getitem__.side_effect = lambda x: motors.get(x, FakeDevice(x))
|
106
|
-
client.device_manager.devices.enabled_devices = list(motors.values())
|
107
|
-
|
108
|
-
# Mock the scans.mv method
|
109
|
-
def mock_mv(*args, relative=False):
|
110
|
-
# Extracting motor and value pairs
|
111
|
-
for i in range(0, len(args), 2):
|
112
|
-
motor = args[i]
|
113
|
-
value = args[i + 1]
|
114
|
-
motor.move(value, relative=relative)
|
115
|
-
return MagicMock(wait=MagicMock()) # Simulate wait method of the move status object
|
116
|
-
|
117
|
-
client.scans = MagicMock(mv=mock_mv)
|
118
|
-
|
119
|
-
# Ensure isinstance check for Positioner passes
|
120
|
-
original_isinstance = isinstance
|
121
|
-
|
122
|
-
def isinstance_mock(obj, class_info):
|
123
|
-
if class_info == Positioner:
|
124
|
-
return True
|
125
|
-
return original_isinstance(obj, class_info)
|
126
|
-
|
127
|
-
with patch("builtins.isinstance", new=isinstance_mock):
|
128
|
-
yield client
|
129
|
-
|
130
57
|
|
131
58
|
#######################################################
|
132
59
|
# Motor Thread
|
@@ -140,7 +67,7 @@ def motor_thread(mocked_client):
|
|
140
67
|
def test_motor_thread_initialization(mocked_client):
|
141
68
|
motor_thread = MotorThread(client=mocked_client)
|
142
69
|
assert motor_thread.client == mocked_client
|
143
|
-
assert isinstance(motor_thread.dev,
|
70
|
+
assert isinstance(motor_thread.dev, DeviceContainer)
|
144
71
|
|
145
72
|
|
146
73
|
def test_get_all_motors_names(mocked_client):
|
@@ -175,12 +102,16 @@ def test_move_motor_absolute_by_run(mocked_client):
|
|
175
102
|
|
176
103
|
def test_move_motor_relative_by_run(mocked_client):
|
177
104
|
motor_thread = MotorThread(client=mocked_client)
|
105
|
+
|
106
|
+
initial_value = motor_thread.dev["samx"].read()["samx"]["value"]
|
107
|
+
move_value = 2.0
|
108
|
+
expected_value = initial_value + move_value
|
178
109
|
motor_thread.motor = "samx"
|
179
|
-
motor_thread.value =
|
110
|
+
motor_thread.value = move_value
|
180
111
|
motor_thread.action = MotorActions.MOVE_RELATIVE
|
181
112
|
motor_thread.run()
|
182
113
|
|
183
|
-
assert mocked_client.device_manager.devices["samx"].read_value ==
|
114
|
+
assert mocked_client.device_manager.devices["samx"].read_value == expected_value
|
184
115
|
|
185
116
|
|
186
117
|
def test_motor_thread_move_absolute(motor_thread):
|
@@ -291,8 +222,12 @@ def test_absolute_initialization(motor_absolute_widget):
|
|
291
222
|
|
292
223
|
|
293
224
|
def test_absolute_save_current_coordinates(motor_absolute_widget):
|
294
|
-
motor_absolute_widget.client.device_manager["samx"].
|
295
|
-
|
225
|
+
motor_x_value = motor_absolute_widget.client.device_manager.devices["samx"].read()["samx"][
|
226
|
+
"value"
|
227
|
+
]
|
228
|
+
motor_y_value = motor_absolute_widget.client.device_manager.devices["samy"].read()["samy"][
|
229
|
+
"value"
|
230
|
+
]
|
296
231
|
motor_absolute_widget.change_motors("samx", "samy")
|
297
232
|
|
298
233
|
emitted_coordinates = []
|
@@ -305,8 +240,7 @@ def test_absolute_save_current_coordinates(motor_absolute_widget):
|
|
305
240
|
# Trigger saving current coordinates
|
306
241
|
motor_absolute_widget.pushButton_save.click()
|
307
242
|
|
308
|
-
|
309
|
-
assert emitted_coordinates == [(2.0, 3.0)]
|
243
|
+
assert emitted_coordinates == [(motor_x_value, motor_y_value)]
|
310
244
|
|
311
245
|
|
312
246
|
def test_absolute_set_absolute_coordinates(motor_absolute_widget):
|
tests/test_motor_map.py
CHANGED
@@ -5,6 +5,8 @@ import pytest
|
|
5
5
|
|
6
6
|
from bec_widgets.widgets import MotorMap
|
7
7
|
|
8
|
+
from .client_mocks import mocked_client
|
9
|
+
|
8
10
|
CONFIG_DEFAULT = {
|
9
11
|
"plot_settings": {
|
10
12
|
"colormap": "Greys",
|
@@ -61,68 +63,6 @@ CONFIG_ONE_DEVICE = {
|
|
61
63
|
}
|
62
64
|
|
63
65
|
|
64
|
-
class FakeDevice:
|
65
|
-
"""Fake minimal positioner class for testing."""
|
66
|
-
|
67
|
-
def __init__(self, name, enabled=True, limits=None, read_value=1.0):
|
68
|
-
self.name = name
|
69
|
-
self.enabled = enabled
|
70
|
-
self.signals = {self.name: {"value": 1.0}}
|
71
|
-
self.description = {self.name: {"source": self.name}}
|
72
|
-
self.limits = limits if limits is not None else [0, 0]
|
73
|
-
self.read_value = read_value
|
74
|
-
|
75
|
-
def set_read_value(self, value):
|
76
|
-
self.read_value = value
|
77
|
-
|
78
|
-
def read(self):
|
79
|
-
return {self.name: {"value": self.read_value}}
|
80
|
-
|
81
|
-
def set_limits(self, limits):
|
82
|
-
self.limits = limits
|
83
|
-
|
84
|
-
def __contains__(self, item):
|
85
|
-
return item == self.name
|
86
|
-
|
87
|
-
@property
|
88
|
-
def _hints(self):
|
89
|
-
return [self.name]
|
90
|
-
|
91
|
-
def set_value(self, fake_value: float = 1.0) -> None:
|
92
|
-
"""
|
93
|
-
Setup fake value for device readout
|
94
|
-
Args:
|
95
|
-
fake_value(float): Desired fake value
|
96
|
-
"""
|
97
|
-
self.signals[self.name]["value"] = fake_value
|
98
|
-
|
99
|
-
def describe(self) -> dict:
|
100
|
-
"""
|
101
|
-
Get the description of the device
|
102
|
-
Returns:
|
103
|
-
dict: Description of the device
|
104
|
-
"""
|
105
|
-
return self.description
|
106
|
-
|
107
|
-
|
108
|
-
@pytest.fixture
|
109
|
-
def mocked_client():
|
110
|
-
client = MagicMock()
|
111
|
-
|
112
|
-
# Mocking specific motors with their limits
|
113
|
-
motors = {
|
114
|
-
"samx": FakeDevice("samx", limits=[-10, 10], read_value=2.0),
|
115
|
-
"samy": FakeDevice("samy", limits=[-5, 5], read_value=3.0),
|
116
|
-
"aptrx": FakeDevice("aptrx", read_value=4.0),
|
117
|
-
"aptry": FakeDevice("aptry", read_value=5.0),
|
118
|
-
}
|
119
|
-
|
120
|
-
client.device_manager.devices = MagicMock()
|
121
|
-
client.device_manager.devices.__getitem__.side_effect = lambda x: motors.get(x, FakeDevice(x))
|
122
|
-
|
123
|
-
return client
|
124
|
-
|
125
|
-
|
126
66
|
@pytest.fixture(scope="function")
|
127
67
|
def motor_map(qtbot, mocked_client):
|
128
68
|
widget = MotorMap(client=mocked_client)
|
@@ -144,12 +84,15 @@ def test_motor_limits_initialization(motor_map):
|
|
144
84
|
|
145
85
|
def test_motor_initial_position(motor_map):
|
146
86
|
motor_map.precision = 2
|
87
|
+
|
88
|
+
motor_map_dev = motor_map.client.device_manager.devices
|
89
|
+
|
147
90
|
# Example test to check if motor initial positions are correctly initialized
|
148
91
|
expected_positions = {
|
149
|
-
("samx", "samx"):
|
150
|
-
("samy", "samy"):
|
151
|
-
("aptrx", "aptrx"):
|
152
|
-
("aptry", "aptry"):
|
92
|
+
("samx", "samx"): motor_map_dev["samx"].read()["samx"]["value"],
|
93
|
+
("samy", "samy"): motor_map_dev["samy"].read()["samy"]["value"],
|
94
|
+
("aptrx", "aptrx"): motor_map_dev["aptrx"].read()["aptrx"]["value"],
|
95
|
+
("aptry", "aptry"): motor_map_dev["aptry"].read()["aptry"]["value"],
|
153
96
|
}
|
154
97
|
for (motor_name, entry), expected_position in expected_positions.items():
|
155
98
|
actual_position = motor_map._get_motor_init_position(motor_name, entry)
|
tests/test_waveform1d.py
CHANGED
@@ -81,8 +81,20 @@ def test_create_waveform1D_by_config(bec_figure):
|
|
81
81
|
"source": "scan_segment",
|
82
82
|
"signals": {
|
83
83
|
"source": "scan_segment",
|
84
|
-
"x": {
|
85
|
-
|
84
|
+
"x": {
|
85
|
+
"name": "samx",
|
86
|
+
"entry": "samx",
|
87
|
+
"unit": None,
|
88
|
+
"modifier": None,
|
89
|
+
"limits": None,
|
90
|
+
},
|
91
|
+
"y": {
|
92
|
+
"name": "bpm4i",
|
93
|
+
"entry": "bpm4i",
|
94
|
+
"unit": None,
|
95
|
+
"modifier": None,
|
96
|
+
"limits": None,
|
97
|
+
},
|
86
98
|
},
|
87
99
|
},
|
88
100
|
"curve-custom": {
|
@@ -218,8 +230,8 @@ def test_change_curve_appearance_methods(bec_figure, qtbot):
|
|
218
230
|
assert c1.config.source == "scan_segment"
|
219
231
|
assert c1.config.signals.model_dump() == {
|
220
232
|
"source": "scan_segment",
|
221
|
-
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None},
|
222
|
-
"y": {"name": "bpm4i", "entry": "bpm4i", "unit": None, "modifier": None},
|
233
|
+
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None, "limits": None},
|
234
|
+
"y": {"name": "bpm4i", "entry": "bpm4i", "unit": None, "modifier": None, "limits": None},
|
223
235
|
}
|
224
236
|
|
225
237
|
|
@@ -246,8 +258,8 @@ def test_change_curve_appearance_args(bec_figure):
|
|
246
258
|
assert c1.config.source == "scan_segment"
|
247
259
|
assert c1.config.signals.model_dump() == {
|
248
260
|
"source": "scan_segment",
|
249
|
-
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None},
|
250
|
-
"y": {"name": "bpm4i", "entry": "bpm4i", "unit": None, "modifier": None},
|
261
|
+
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None, "limits": None},
|
262
|
+
"y": {"name": "bpm4i", "entry": "bpm4i", "unit": None, "modifier": None, "limits": None},
|
251
263
|
}
|
252
264
|
|
253
265
|
|
@@ -339,8 +351,14 @@ def test_curve_add_by_config(bec_figure):
|
|
339
351
|
"source": "scan_segment",
|
340
352
|
"signals": {
|
341
353
|
"source": "scan_segment",
|
342
|
-
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None},
|
343
|
-
"y": {
|
354
|
+
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None, "limits": None},
|
355
|
+
"y": {
|
356
|
+
"name": "bpm4i",
|
357
|
+
"entry": "bpm4i",
|
358
|
+
"unit": None,
|
359
|
+
"modifier": None,
|
360
|
+
"limits": None,
|
361
|
+
},
|
344
362
|
},
|
345
363
|
}
|
346
364
|
|
File without changes
|
File without changes
|
File without changes
|