bec-widgets 0.44.5__py3-none-any.whl → 0.46.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 +123 -1
- bec_widgets/cli/generate_cli.py +4 -3
- bec_widgets/cli/server.py +2 -2
- bec_widgets/widgets/__init__.py +1 -2
- bec_widgets/widgets/figure/figure.py +148 -22
- bec_widgets/widgets/plots/__init__.py +2 -1
- bec_widgets/widgets/plots/motor_map.py +423 -0
- bec_widgets/widgets/plots/plot_base.py +1 -1
- bec_widgets/widgets/plots/{waveform1d.py → waveform.py} +77 -11
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.46.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.46.0.dist-info}/RECORD +23 -24
- tests/client_mocks.py +76 -31
- tests/test_bec_dispatcher.py +2 -2
- tests/test_bec_figure.py +28 -4
- 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 +75 -10
- bec_widgets/widgets/monitor_scatter_2D/__init__.py +0 -1
- bec_widgets/widgets/monitor_scatter_2D/monitor_scatter_2D.py +0 -374
- tests/test_bec_monitor_scatter2D.py +0 -162
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.46.0.dist-info}/LICENSE +0 -0
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.46.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.44.5.dist-info → bec_widgets-0.46.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
@@ -4,7 +4,7 @@ from unittest.mock import MagicMock
|
|
4
4
|
import numpy as np
|
5
5
|
import pytest
|
6
6
|
|
7
|
-
from bec_widgets.widgets.plots.
|
7
|
+
from bec_widgets.widgets.plots.waveform import CurveConfig, Signal, SignalData
|
8
8
|
|
9
9
|
from .client_mocks import mocked_client
|
10
10
|
from .test_bec_figure import bec_figure
|
@@ -49,7 +49,7 @@ def test_adding_curve_with_same_id(bec_figure):
|
|
49
49
|
|
50
50
|
def test_create_waveform1D_by_config(bec_figure):
|
51
51
|
w1_config_input = {
|
52
|
-
"widget_class": "
|
52
|
+
"widget_class": "BECWaveform",
|
53
53
|
"gui_id": "widget_1",
|
54
54
|
"parent_id": "BECFigure_1708689320.788527",
|
55
55
|
"row": 0,
|
@@ -73,6 +73,7 @@ def test_create_waveform1D_by_config(bec_figure):
|
|
73
73
|
"parent_id": "widget_1",
|
74
74
|
"label": "bpm4i-bpm4i",
|
75
75
|
"color": "#cc4778",
|
76
|
+
"colormap": "plasma",
|
76
77
|
"symbol": "o",
|
77
78
|
"symbol_color": None,
|
78
79
|
"symbol_size": 5,
|
@@ -81,8 +82,21 @@ def test_create_waveform1D_by_config(bec_figure):
|
|
81
82
|
"source": "scan_segment",
|
82
83
|
"signals": {
|
83
84
|
"source": "scan_segment",
|
84
|
-
"x": {
|
85
|
-
|
85
|
+
"x": {
|
86
|
+
"name": "samx",
|
87
|
+
"entry": "samx",
|
88
|
+
"unit": None,
|
89
|
+
"modifier": None,
|
90
|
+
"limits": None,
|
91
|
+
},
|
92
|
+
"y": {
|
93
|
+
"name": "bpm4i",
|
94
|
+
"entry": "bpm4i",
|
95
|
+
"unit": None,
|
96
|
+
"modifier": None,
|
97
|
+
"limits": None,
|
98
|
+
},
|
99
|
+
"z": None,
|
86
100
|
},
|
87
101
|
},
|
88
102
|
"curve-custom": {
|
@@ -91,6 +105,7 @@ def test_create_waveform1D_by_config(bec_figure):
|
|
91
105
|
"parent_id": "widget_1",
|
92
106
|
"label": "curve-custom",
|
93
107
|
"color": "blue",
|
108
|
+
"colormap": "plasma",
|
94
109
|
"symbol": "o",
|
95
110
|
"symbol_color": None,
|
96
111
|
"symbol_size": 5,
|
@@ -218,8 +233,9 @@ def test_change_curve_appearance_methods(bec_figure, qtbot):
|
|
218
233
|
assert c1.config.source == "scan_segment"
|
219
234
|
assert c1.config.signals.model_dump() == {
|
220
235
|
"source": "scan_segment",
|
221
|
-
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None},
|
222
|
-
"y": {"name": "bpm4i", "entry": "bpm4i", "unit": None, "modifier": None},
|
236
|
+
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None, "limits": None},
|
237
|
+
"y": {"name": "bpm4i", "entry": "bpm4i", "unit": None, "modifier": None, "limits": None},
|
238
|
+
"z": None,
|
223
239
|
}
|
224
240
|
|
225
241
|
|
@@ -246,8 +262,9 @@ def test_change_curve_appearance_args(bec_figure):
|
|
246
262
|
assert c1.config.source == "scan_segment"
|
247
263
|
assert c1.config.signals.model_dump() == {
|
248
264
|
"source": "scan_segment",
|
249
|
-
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None},
|
250
|
-
"y": {"name": "bpm4i", "entry": "bpm4i", "unit": None, "modifier": None},
|
265
|
+
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None, "limits": None},
|
266
|
+
"y": {"name": "bpm4i", "entry": "bpm4i", "unit": None, "modifier": None, "limits": None},
|
267
|
+
"z": None,
|
251
268
|
}
|
252
269
|
|
253
270
|
|
@@ -331,6 +348,7 @@ def test_curve_add_by_config(bec_figure):
|
|
331
348
|
"parent_id": "widget_1",
|
332
349
|
"label": "bpm4i-bpm4i",
|
333
350
|
"color": "#cc4778",
|
351
|
+
"colormap": "plasma",
|
334
352
|
"symbol": "o",
|
335
353
|
"symbol_color": None,
|
336
354
|
"symbol_size": 5,
|
@@ -339,8 +357,15 @@ def test_curve_add_by_config(bec_figure):
|
|
339
357
|
"source": "scan_segment",
|
340
358
|
"signals": {
|
341
359
|
"source": "scan_segment",
|
342
|
-
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None},
|
343
|
-
"y": {
|
360
|
+
"x": {"name": "samx", "entry": "samx", "unit": None, "modifier": None, "limits": None},
|
361
|
+
"y": {
|
362
|
+
"name": "bpm4i",
|
363
|
+
"entry": "bpm4i",
|
364
|
+
"unit": None,
|
365
|
+
"modifier": None,
|
366
|
+
"limits": None,
|
367
|
+
},
|
368
|
+
"z": None,
|
344
369
|
},
|
345
370
|
}
|
346
371
|
|
@@ -410,3 +435,43 @@ def test_scan_history_with_val_access(bec_figure, qtbot):
|
|
410
435
|
|
411
436
|
assert np.array_equal(x_data, [1, 2, 3])
|
412
437
|
assert np.array_equal(y_data, [4, 5, 6])
|
438
|
+
|
439
|
+
|
440
|
+
def test_scatter_2d_update(bec_figure, qtbot):
|
441
|
+
w1 = bec_figure.add_plot()
|
442
|
+
|
443
|
+
c1 = w1.add_curve_scan(x_name="samx", y_name="samx", z_name="bpm4i")
|
444
|
+
|
445
|
+
msg = {
|
446
|
+
"data": {
|
447
|
+
"samx": {"samx": {"value": [1, 2, 3]}},
|
448
|
+
"samy": {"samy": {"value": [4, 5, 6]}},
|
449
|
+
"bpm4i": {"bpm4i": {"value": [1, 3, 2]}},
|
450
|
+
},
|
451
|
+
"scan_id": 1,
|
452
|
+
}
|
453
|
+
msg_metadata = {"scan_name": "line_scan"}
|
454
|
+
|
455
|
+
mock_scan_data = MagicMock()
|
456
|
+
mock_scan_data.data = {
|
457
|
+
device_name: {
|
458
|
+
entry: MagicMock(val=msg["data"][device_name][entry]["value"])
|
459
|
+
for entry in msg["data"][device_name]
|
460
|
+
}
|
461
|
+
for device_name in msg["data"]
|
462
|
+
}
|
463
|
+
|
464
|
+
w1.queue.scan_storage.find_scan_by_ID.return_value = mock_scan_data
|
465
|
+
|
466
|
+
w1.on_scan_segment(msg, msg_metadata)
|
467
|
+
qtbot.wait(500)
|
468
|
+
|
469
|
+
data = c1.get_data()
|
470
|
+
expected_x_y_data = ([1, 2, 3], [1, 2, 3])
|
471
|
+
expected_z_colors = w1._make_z_gradient([1, 3, 2], "plasma")
|
472
|
+
|
473
|
+
scatter_points = c1.scatter.points()
|
474
|
+
colors = [point.brush().color() for point in scatter_points]
|
475
|
+
|
476
|
+
assert np.array_equal(data, expected_x_y_data)
|
477
|
+
assert colors == expected_z_colors
|
@@ -1 +0,0 @@
|
|
1
|
-
from .monitor_scatter_2D import BECMonitor2DScatter
|