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.
@@ -2,7 +2,7 @@
2
2
  from unittest.mock import MagicMock, patch
3
3
 
4
4
  import pytest
5
- from bec_lib.device import Positioner
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, MagicMock)
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 = 2.0
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 == 4.0
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"].set_value(2.0)
295
- motor_absolute_widget.client.device_manager["samy"].set_value(3.0)
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
- # Default position of samx and samy are 2.0 and 3.0 respectively
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"): 2.0,
150
- ("samy", "samy"): 3.0,
151
- ("aptrx", "aptrx"): 4.0,
152
- ("aptry", "aptry"): 5.0,
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.waveform1d import CurveConfig, Signal, SignalData
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": "BECWaveform1D",
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": {"name": "samx", "entry": "samx", "unit": None, "modifier": None},
85
- "y": {"name": "bpm4i", "entry": "bpm4i", "unit": None, "modifier": None},
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": {"name": "bpm4i", "entry": "bpm4i", "unit": None, "modifier": None},
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