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.
@@ -1,9 +1,9 @@
1
1
  bec_widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  bec_widgets/cli/__init__.py,sha256=yFyAmDteCcndbReunhnrfDib6JujQ7-BKWeVvuU0Ylw,30
3
- bec_widgets/cli/client.py,sha256=LHcqlz2PPt_b2BN5YGJLlMLcC1zHdNKRpj3F0pq8mus,34044
3
+ bec_widgets/cli/client.py,sha256=rIK2W4rh6dSnrDkbw-qPOdE34r2sTwpOEhHR_QG81bM,38222
4
4
  bec_widgets/cli/client_utils.py,sha256=8tVOmAMo5x0tUywTfivvCt8n0tJoT5WzoCa3GtsZ-qE,9694
5
- bec_widgets/cli/generate_cli.py,sha256=s0huOR9DSoe69F4OemS4vj5ZQ_w9cCuFvPhuBmDJJ1g,3976
6
- bec_widgets/cli/server.py,sha256=4552dbbdJlalIllQGJFR3AsN0iLl5Qs23BAeqQiof0s,4721
5
+ bec_widgets/cli/generate_cli.py,sha256=JLqUlUgfz_f_4KHPRUAN-Xli-K7uNOc8-F-LkAC7Scw,4004
6
+ bec_widgets/cli/server.py,sha256=IqpFG7FnsTejnq-4reAqk5mBpynk1jZgoTn7O43N_6I,4717
7
7
  bec_widgets/examples/__init__.py,sha256=WWQ0cu7m8sA4Ehy-DWdTIqSISjaHsbxhsNmNrMnhDZU,202
8
8
  bec_widgets/examples/eiger_plot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  bec_widgets/examples/eiger_plot/eiger_plot.py,sha256=Uxl2Usf8jEzaX7AT8zVqa1x8ZIEgI1HmazSlb-tRFWE,10359
@@ -36,19 +36,17 @@ bec_widgets/utils/widget_io.py,sha256=JKl508VnqQSxcaHqKaoBQ1TWSOm3pXhxQGx7iF_pRA
36
36
  bec_widgets/utils/yaml_dialog.py,sha256=soZI8BOjlqYGfYDga70MEvkxJTsktq4y7B3uog2cSik,1851
37
37
  bec_widgets/validation/__init__.py,sha256=ismd1bU5FhFb0zFPwNKuq7oT48G4Y2GfaMZOdNKUtGk,132
38
38
  bec_widgets/validation/monitor_config_validator.py,sha256=M9p8K_nvxicnqJB4X7j90R377WHYVH4wMCtSXsRI51M,8150
39
- bec_widgets/widgets/__init__.py,sha256=UMySMLbB53R-ygLz7XOdfRsXxwhEzkGQ5fool3E1_Kc,437
39
+ bec_widgets/widgets/__init__.py,sha256=GptryTiWJ4yWZZVBG_03guISJabSOzVpOMRkgW0LdTg,383
40
40
  bec_widgets/widgets/editor/__init__.py,sha256=5mBdFYi_IpygCz81kbLEZUWhd1b6oqiO3nASejuV_ug,30
41
41
  bec_widgets/widgets/editor/editor.py,sha256=pIIYLPqqqhXqT11Xj10cyGEiy-ieNGE4ZujN5lf0e68,15110
42
42
  bec_widgets/widgets/figure/__init__.py,sha256=3hGx_KOV7QHCYAV06aNuUgKq4QIYCjUTad-DrwkUaBM,44
43
- bec_widgets/widgets/figure/figure.py,sha256=iMsK2M4cGjLyj0hEGqJ1cHqFMAcRVu_kon6GOwwpHzk,29915
43
+ bec_widgets/widgets/figure/figure.py,sha256=7kcHjkml3roJCVqMvlrDI3h_b4gSt2GNHm-JcTWHnDk,34361
44
44
  bec_widgets/widgets/figure/figure_debug_minimal.ui,sha256=GodXBvBvs5QAUsHbo3pcxR4o51Tvce4DTqpTluk3hOs,742
45
45
  bec_widgets/widgets/monitor/__init__.py,sha256=afXuZcBOxNAuYdCkIQXX5J60R5A3Q_86lNEW2vpFtPI,32
46
46
  bec_widgets/widgets/monitor/config_dialog.py,sha256=Z1a4WRIVlfEGdwC-QG25kba2EHCZWi5J843tBVZlWiI,20275
47
47
  bec_widgets/widgets/monitor/config_dialog.ui,sha256=ISMcF7CLTAMXhfZh2Yv5yezzAjMtb9fxY1pmX4B_jCg,5932
48
48
  bec_widgets/widgets/monitor/monitor.py,sha256=ET5O48kqRlwd8_jCMCrp8QwnL8LvoNgb5nQmVw36pKg,30219
49
49
  bec_widgets/widgets/monitor/tab_template.ui,sha256=JVB5fkzVhTjxMn9EdZCXsnfzBkeZpFMdTRuqTNSDSSo,4904
50
- bec_widgets/widgets/monitor_scatter_2D/__init__.py,sha256=hgiRKV5PBdesYLFbLmYb-qfqNTIGwzpdQFvkLi6-qnE,52
51
- bec_widgets/widgets/monitor_scatter_2D/monitor_scatter_2D.py,sha256=AKx3SUKxbyUxpXAHG5T-_horr02coKHbcMQppRZ_eEY,13108
52
50
  bec_widgets/widgets/motor_control/__init__.py,sha256=_4-G9AKcEnOyS6qVE26meRw3grEohNeMzACNMU5Sig0,153
53
51
  bec_widgets/widgets/motor_control/motor_control.py,sha256=sfH_uGes4B3mOFPP9BYVMtqMSCFD5fWJNQwNxV06288,42458
54
52
  bec_widgets/widgets/motor_control/motor_control_absolute.ui,sha256=nR3p6oevAkIBTLW5wM_zYOVWsCAUgeMZdRm10Q77COE,4126
@@ -57,40 +55,41 @@ bec_widgets/widgets/motor_control/motor_control_selection.ui,sha256=vXXpvNWuL6xy
57
55
  bec_widgets/widgets/motor_control/motor_control_table.ui,sha256=t6aRKiSmutMfp0AyupavbCs0cal-FANEnlKQiPzC9PQ,2792
58
56
  bec_widgets/widgets/motor_map/__init__.py,sha256=K3c-3A_LbxK0UJ0_bV3opL-wGLTwBLendsJXsg8GAqE,32
59
57
  bec_widgets/widgets/motor_map/motor_map.py,sha256=A7VOs8RAiXaLZF4_oxOYJX5BwMhRvir0k0u5eEPFtLc,21909
60
- bec_widgets/widgets/plots/__init__.py,sha256=aS4zo2SSLK9pO6FdKrSC-dUigsQP3c50IJ81QPJfwBs,190
58
+ bec_widgets/widgets/plots/__init__.py,sha256=kGQTORTr-2M9vmVCK-T7AFre4bY5LVVzGxcIzT81-ZU,237
61
59
  bec_widgets/widgets/plots/image.py,sha256=Ty-8etIYBQl1MJLcuwCtlLUcAuXbxBq4RLDikbTL34c,31748
62
- bec_widgets/widgets/plots/plot_base.py,sha256=aek2O0JNOt8mjHGJRCrKtF4jsbR55tpEHNb8sSrwrd4,8434
63
- bec_widgets/widgets/plots/waveform1d.py,sha256=AstOAq_zK2y1ej2HV-EpOh-w3e-RGOyNY0q9f2iWa-Q,25508
60
+ bec_widgets/widgets/plots/motor_map.py,sha256=wNluFsen4y68HMOu3XJu3tzmtJ4xilmOh8scuRiGUqw,15113
61
+ bec_widgets/widgets/plots/plot_base.py,sha256=tF0oamgCVIXFrfD8jnMVTltkzYLLSQ0Dq9HAG57Ji3E,8446
62
+ bec_widgets/widgets/plots/waveform.py,sha256=xSvq9N296L0JryV8quz0MpLIG37E-3hjgfWn_EeMkaw,28504
64
63
  bec_widgets/widgets/scan_control/__init__.py,sha256=IOfHl15vxb_uC6KN62-PeUzbBha_vQyqkkXbJ2HU674,38
65
64
  bec_widgets/widgets/scan_control/scan_control.py,sha256=tbO9tbVynRvs4VCxTZ4ZFBDTVAojIr-zkl70vuHbWgw,17116
66
65
  bec_widgets/widgets/toolbar/__init__.py,sha256=d-TP4_cr_VbpwreMM4ePnfZ5YXsEPQ45ibEf75nuGoE,36
67
66
  bec_widgets/widgets/toolbar/toolbar.py,sha256=sxz7rbc8XNPS6n2WMObF4-2PqdYfPxVtsOZEGV6mqa0,5124
68
67
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
- tests/client_mocks.py,sha256=UUKX-c4p95GJ8x882FRhdkjO7aG6GtBnm3DbHbdCqoc,2105
68
+ tests/client_mocks.py,sha256=925yW3vFnS8h6ym_4kpgX299WGVeObkGneuiXNWRE7s,3596
70
69
  tests/conftest.py,sha256=wMFVnerrLZjpi-OnPf7Ugah_DCb51QD1WWcbR4e_M6k,1055
71
70
  tests/test_bec_connector.py,sha256=f2XXGGw3NoZLIUrDuZuEWwF_ttOYmmquCgUrV5XkIOY,1951
72
- tests/test_bec_dispatcher.py,sha256=vWQGdUyMvssrOyiVxdsOpErOh4awX_NSQWvKSBoRfAI,9223
73
- tests/test_bec_figure.py,sha256=9X9GOakh7pSzofAqloG4NCHhTc1Rc5bDENf5dYHLtsk,7070
74
- tests/test_bec_monitor.py,sha256=I3GOEHcxqbRsBA_cYO8MBCnZkkJF8MAkRJczSgjJBAo,8933
75
- tests/test_bec_monitor_scatter2D.py,sha256=UYs0pe9uBX7GpFVkhxw-Hx_MwTz7NKMtWIBqONTZ1JI,5797
76
- tests/test_config_dialog.py,sha256=V-4coDZi4DfXDHDsjG75Yd7VyBxweybiamWbMeUY4uo,8774
71
+ tests/test_bec_dispatcher.py,sha256=aX4ngQEslgxqYb0wpIYAz2_WUfGO6jJxYrPRy5Y4Xo8,9239
72
+ tests/test_bec_figure.py,sha256=4vOVZMRb50aoQaSjry-oomcjgG3lAQXtZJIq-FwkM5I,7908
73
+ tests/test_bec_monitor.py,sha256=mN7gBY7oXY6j65zzihpy8r-FvwVoCQlie3F6SoVq0mo,7042
74
+ tests/test_bec_motor_map.py,sha256=IXSfitUGxOPqmngwVNPK5nwi2QDcXWjBkGNb0dBZDxQ,4611
75
+ tests/test_config_dialog.py,sha256=5uNGcpvrx8qDdMwFCTXr8HMzFZF4rFi-ZHoDpMxGMf8,6955
77
76
  tests/test_crosshair.py,sha256=d7fX-ymboZPALNqqiAj86PZ96llmGZ_3jf0yjVP0S94,5039
78
77
  tests/test_editor.py,sha256=TED5k1xFJHRZ4KDAg2VxSRu_hMJnra-lbAmVwsDicsM,6784
79
78
  tests/test_eiger_plot.py,sha256=bWnKBQid0YcLMQeBLy6ojb4ZpwTG-rFVT0kMg9Y08p8,4427
80
79
  tests/test_generate_cli_client.py,sha256=BdpTZMNUFOBJa2e-rme9AJUoXfueYyLiUCOpGi3SNvc,2400
81
- tests/test_motor_control.py,sha256=Ba6AQqc-rh-iezciy1qnqKTl65bZVod7UiSxri7Rxsc,22581
82
- tests/test_motor_map.py,sha256=rfu6i8Vd_O0xSzFDKNWtxoWQah6gImGwqTR6UPfaeVg,7446
80
+ tests/test_motor_control.py,sha256=jdTG35z3jOL9XCAIDNIGfdv60vcwGLHa3KJjKqJkoZw,20322
81
+ tests/test_motor_map.py,sha256=UEjmtIYI2mxq9BUeopqoqNNy7UiPJEts9h45ufsFcrA,5979
83
82
  tests/test_plot_base.py,sha256=bOdlgAxh9oKk5PwiQ_MSFmzr44uJ61Tlg242RCIhl5c,2610
84
83
  tests/test_scan_control.py,sha256=e6_YpyxcayK35jXBSfxinGNL_8G-jcrWap25eg3z4QI,7560
85
84
  tests/test_stream_plot.py,sha256=LNCYIj9CafremGaz-DwDktCRJRrjgfOdVewCUwwZE5s,5843
86
85
  tests/test_validator_errors.py,sha256=NFxyv0TIOXeZKZRRUBfVQ7bpunwY4KkG95yTUdQmvns,3532
87
- tests/test_waveform1d.py,sha256=FjJh6YkiT09NvZqW144Ar86oGLDHi6WYh1bhhBlj-6g,13135
86
+ tests/test_waveform1d.py,sha256=-YfBi_m91sR_v4oj8rshARk4G6AOxqQB_gbVqJ3iXvY,15003
88
87
  tests/test_widget_io.py,sha256=FeL3ZYSBQnRt6jxj8VGYw1cmcicRQyHKleahw7XIyR0,3475
89
88
  tests/test_yaml_dialog.py,sha256=HNrqferkdg02-9ieOhhI2mr2Qvt7GrYgXmQ061YCTbg,5794
90
89
  tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
90
  tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
92
- bec_widgets-0.44.5.dist-info/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
93
- bec_widgets-0.44.5.dist-info/METADATA,sha256=I_tO6_2mn-R9i9Y_kR-I-9sSdOWgI9MRGYm981Bge4Q,3714
94
- bec_widgets-0.44.5.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
95
- bec_widgets-0.44.5.dist-info/top_level.txt,sha256=EXCwhJYmXmd1DjYYL3hrGsddX-97IwYSiIHrf27FFVk,18
96
- bec_widgets-0.44.5.dist-info/RECORD,,
91
+ bec_widgets-0.46.0.dist-info/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
92
+ bec_widgets-0.46.0.dist-info/METADATA,sha256=DMGzNpuWh8HzUrKOPVwMYr_5AsH5dVyGStDXZr85pqs,3714
93
+ bec_widgets-0.46.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
94
+ bec_widgets-0.46.0.dist-info/top_level.txt,sha256=EXCwhJYmXmd1DjYYL3hrGsddX-97IwYSiIHrf27FFVk,18
95
+ bec_widgets-0.46.0.dist-info/RECORD,,
tests/client_mocks.py CHANGED
@@ -1,8 +1,9 @@
1
1
  # pylint: disable = no-name-in-module,missing-class-docstring, missing-module-docstring
2
-
3
- from unittest.mock import MagicMock
2
+ from unittest.mock import MagicMock, patch
4
3
 
5
4
  import pytest
5
+ from bec_lib.device import Positioner
6
+ from bec_lib.devicemanager import DeviceContainer
6
7
 
7
8
 
8
9
  class FakeDevice:
@@ -12,7 +13,7 @@ class FakeDevice:
12
13
  self.name = name
13
14
  self.enabled = enabled
14
15
  self.signals = {self.name: {"value": 1.0}}
15
- self.description = {self.name: {"source": self.name}}
16
+ self.description = {self.name: {"source": self.name, "dtype": "number", "shape": []}}
16
17
 
17
18
  def __contains__(self, item):
18
19
  return item == self.name
@@ -38,41 +39,85 @@ class FakeDevice:
38
39
  return self.description
39
40
 
40
41
 
41
- def get_mocked_device(device_name: str):
42
- """
43
- Helper function to mock the devices
44
- Args:
45
- device_name(str): Name of the device to mock
46
- """
47
- return FakeDevice(name=device_name, enabled=True)
42
+ class FakePositioner(FakeDevice):
43
+ def __init__(self, name, enabled=True, limits=None, read_value=1.0):
44
+ super().__init__(name, enabled)
45
+ self.limits = limits if limits is not None else [0, 0]
46
+ self.read_value = read_value
47
+
48
+ def set_read_value(self, value):
49
+ self.read_value = value
50
+
51
+ def read(self):
52
+ return {self.name: {"value": self.read_value}}
53
+
54
+ def set_limits(self, limits):
55
+ self.limits = limits
56
+
57
+ def move(self, value, relative=False):
58
+ """Simulates moving the device to a new position."""
59
+ if relative:
60
+ self.read_value += value
61
+ else:
62
+ self.read_value = value
63
+ # Respect the limits
64
+ self.read_value = max(min(self.read_value, self.limits[1]), self.limits[0])
65
+
66
+ @property
67
+ def readback(self):
68
+ return MagicMock(get=MagicMock(return_value=self.read_value))
69
+
70
+
71
+ class DMMock:
72
+ def __init__(self):
73
+ self.devices = DeviceContainer()
74
+
75
+ def add_devives(self, devices: list):
76
+ for device in devices:
77
+ self.devices[device.name] = device
78
+
79
+
80
+ DEVICES = [
81
+ FakePositioner("samx", limits=[-10, 10], read_value=2.0),
82
+ FakePositioner("samy", limits=[-5, 5], read_value=3.0),
83
+ FakePositioner("aptrx", limits=None, read_value=4.0),
84
+ FakePositioner("aptry", limits=None, read_value=5.0),
85
+ FakeDevice("gauss_bpm"),
86
+ FakeDevice("gauss_adc1"),
87
+ FakeDevice("gauss_adc2"),
88
+ FakeDevice("gauss_adc3"),
89
+ FakeDevice("bpm4i"),
90
+ FakeDevice("bpm3a"),
91
+ FakeDevice("bpm3i"),
92
+ ]
48
93
 
49
94
 
50
95
  @pytest.fixture(scope="function")
51
96
  def mocked_client():
52
- # Create a dictionary of mocked devices
53
- device_names = [
54
- "samx",
55
- "samy",
56
- "gauss_bpm",
57
- "gauss_adc1",
58
- "gauss_adc2",
59
- "gauss_adc3",
60
- "bpm4i",
61
- "bpm3a",
62
- "bpm3i",
63
- ]
64
- mocked_devices = {name: get_mocked_device(name) for name in device_names}
65
-
66
97
  # Create a MagicMock object
67
98
  client = MagicMock()
68
99
 
69
100
  # Mock the device_manager.devices attribute
70
- client.device_manager.devices = MagicMock()
71
- client.device_manager.devices.__getitem__.side_effect = lambda x: mocked_devices.get(x)
72
- client.device_manager.devices.__contains__.side_effect = lambda x: x in mocked_devices
101
+ client.device_manager = DMMock()
102
+ client.device_manager.add_devives(DEVICES)
103
+
104
+ def mock_mv(*args, relative=False):
105
+ # Extracting motor and value pairs
106
+ for i in range(0, len(args), 2):
107
+ motor = args[i]
108
+ value = args[i + 1]
109
+ motor.move(value, relative=relative)
110
+ return MagicMock(wait=MagicMock())
111
+
112
+ client.scans = MagicMock(mv=mock_mv)
113
+
114
+ # Ensure isinstance check for Positioner passes
115
+ original_isinstance = isinstance
73
116
 
74
- # Set each device as an attribute of the mock
75
- for name, device in mocked_devices.items():
76
- setattr(client.device_manager.devices, name, device)
117
+ def isinstance_mock(obj, class_info):
118
+ if class_info == Positioner and isinstance(obj, FakePositioner):
119
+ return True
120
+ return original_isinstance(obj, class_info)
77
121
 
78
- return client
122
+ with patch("builtins.isinstance", new=isinstance_mock):
123
+ yield client
@@ -5,7 +5,7 @@ import pytest
5
5
  from bec_lib.connector import MessageObject
6
6
  from bec_lib.messages import ScanMessage
7
7
 
8
- msg = MessageObject(topic="", value=ScanMessage(point_id=0, scan_id=0, data={}))
8
+ msg = MessageObject(topic="", value=ScanMessage(point_id=0, scan_id="scan_id", data={}))
9
9
 
10
10
 
11
11
  @pytest.fixture(name="consumer")
@@ -205,7 +205,7 @@ def test_connect_one_slot_multiple_topics_single_callback(bec_dispatcher, consum
205
205
  # Simulate messages being published on each topic
206
206
  for topic in topics:
207
207
  msg_with_topic = MessageObject(
208
- topic=topic, value=ScanMessage(point_id=0, scan_id=0, data={})
208
+ topic=topic, value=ScanMessage(point_id=0, scan_id="scan_id", data={})
209
209
  )
210
210
  consumer.register.call_args.kwargs["cb"](msg_with_topic)
211
211
 
tests/test_bec_figure.py CHANGED
@@ -5,7 +5,8 @@ from unittest.mock import MagicMock
5
5
  import numpy as np
6
6
  import pytest
7
7
 
8
- from bec_widgets.widgets import BECFigure
8
+ from bec_widgets.widgets import BECFigure, BECMotorMap, BECWaveform
9
+ from bec_widgets.widgets.plots import BECImageShow
9
10
 
10
11
  from .client_mocks import mocked_client
11
12
 
@@ -49,8 +50,8 @@ def test_bec_figure_add_remove_plot(bec_figure):
49
50
  assert "widget_1" in bec_figure._widgets
50
51
  assert "widget_2" in bec_figure._widgets
51
52
  assert "widget_3" in bec_figure._widgets
52
- assert bec_figure._widgets["widget_1"].config.widget_class == "BECWaveform1D"
53
- assert bec_figure._widgets["widget_2"].config.widget_class == "BECWaveform1D"
53
+ assert bec_figure._widgets["widget_1"].config.widget_class == "BECWaveform"
54
+ assert bec_figure._widgets["widget_2"].config.widget_class == "BECWaveform"
54
55
  assert bec_figure._widgets["widget_3"].config.widget_class == "BECPlotBase"
55
56
 
56
57
  # Check accessing positions by the grid in figure
@@ -63,7 +64,17 @@ def test_bec_figure_add_remove_plot(bec_figure):
63
64
  assert len(bec_figure._widgets) == initial_count + 2
64
65
  assert "widget_1" not in bec_figure._widgets
65
66
  assert "widget_3" in bec_figure._widgets
66
- assert bec_figure._widgets["widget_2"].config.widget_class == "BECWaveform1D"
67
+ assert bec_figure._widgets["widget_2"].config.widget_class == "BECWaveform"
68
+
69
+
70
+ def test_add_different_types_of_widgets(bec_figure):
71
+ plt = bec_figure.plot("samx", "bpm4i")
72
+ im = bec_figure.image("eiger")
73
+ motor_map = bec_figure.motor_map("samx", "samy")
74
+
75
+ assert plt.__class__ == BECWaveform
76
+ assert im.__class__ == BECImageShow
77
+ assert motor_map.__class__ == BECMotorMap
67
78
 
68
79
 
69
80
  def test_access_widgets_access_errors(bec_figure):
@@ -217,3 +228,16 @@ def test_clear_all(bec_figure):
217
228
 
218
229
  assert len(bec_figure._widgets) == 0
219
230
  assert np.shape(bec_figure.grid) == (0,)
231
+
232
+
233
+ def test_shortcuts(bec_figure):
234
+ plt = bec_figure.plot("samx", "bpm4i")
235
+ im = bec_figure.image("eiger")
236
+ motor_map = bec_figure.motor_map("samx", "samy")
237
+
238
+ assert plt.config.widget_class == "BECWaveform"
239
+ assert plt.__class__ == BECWaveform
240
+ assert im.config.widget_class == "BECImageShow"
241
+ assert im.__class__ == BECImageShow
242
+ assert motor_map.config.widget_class == "BECMotorMap"
243
+ assert motor_map.__class__ == BECMotorMap
tests/test_bec_monitor.py CHANGED
@@ -7,6 +7,8 @@ import yaml
7
7
 
8
8
  from bec_widgets.widgets import BECMonitor
9
9
 
10
+ from .client_mocks import mocked_client
11
+
10
12
 
11
13
  def load_test_config(config_name):
12
14
  """Helper function to load config from yaml file."""
@@ -16,69 +18,6 @@ def load_test_config(config_name):
16
18
  return config
17
19
 
18
20
 
19
- class FakeDevice:
20
- """Fake minimal positioner class for testing."""
21
-
22
- def __init__(self, name, enabled=True):
23
- self.name = name
24
- self.enabled = enabled
25
- self.signals = {self.name: {"value": 1.0}}
26
- self.description = {self.name: {"source": self.name}}
27
-
28
- def __contains__(self, item):
29
- return item == self.name
30
-
31
- @property
32
- def _hints(self):
33
- return [self.name]
34
-
35
- def set_value(self, fake_value: float = 1.0) -> None:
36
- """
37
- Setup fake value for device readout
38
- Args:
39
- fake_value(float): Desired fake value
40
- """
41
- self.signals[self.name]["value"] = fake_value
42
-
43
- def describe(self) -> dict:
44
- """
45
- Get the description of the device
46
- Returns:
47
- dict: Description of the device
48
- """
49
- return self.description
50
-
51
-
52
- def get_mocked_device(device_name: str):
53
- """
54
- Helper function to mock the devices
55
- Args:
56
- device_name(str): Name of the device to mock
57
- """
58
- return FakeDevice(name=device_name, enabled=True)
59
-
60
-
61
- @pytest.fixture(scope="function")
62
- def mocked_client():
63
- # Create a dictionary of mocked devices
64
- device_names = ["samx", "gauss_bpm", "gauss_adc1", "gauss_adc2", "gauss_adc3", "bpm4i"]
65
- mocked_devices = {name: get_mocked_device(name) for name in device_names}
66
-
67
- # Create a MagicMock object
68
- client = MagicMock()
69
-
70
- # Mock the device_manager.devices attribute
71
- client.device_manager.devices = MagicMock()
72
- client.device_manager.devices.__getitem__.side_effect = lambda x: mocked_devices.get(x)
73
- client.device_manager.devices.__contains__.side_effect = lambda x: x in mocked_devices
74
-
75
- # Set each device as an attribute of the mock
76
- for name, device in mocked_devices.items():
77
- setattr(client.device_manager.devices, name, device)
78
-
79
- return client
80
-
81
-
82
21
  @pytest.fixture(scope="function")
83
22
  def monitor(bec_dispatcher, qtbot, mocked_client):
84
23
  # client = MagicMock()
@@ -266,9 +205,6 @@ def test_on_scan_segment(monitor, config_name, msg, metadata, expected_data):
266
205
  config = load_test_config(config_name)
267
206
  monitor.on_config_update(config)
268
207
 
269
- # Get hints
270
- monitor.dev.__getitem__.side_effect = mock_getitem
271
-
272
208
  # Mock scan_storage.find_scan_by_ID
273
209
  mock_scan_data = MagicMock()
274
210
  mock_scan_data.data = {
@@ -0,0 +1,125 @@
1
+ import pytest
2
+
3
+ from bec_widgets.widgets import BECMotorMap
4
+ from bec_widgets.widgets.plots.motor_map import MotorMapConfig
5
+ from bec_widgets.widgets.plots.waveform import Signal, SignalData
6
+
7
+ from .client_mocks import mocked_client
8
+
9
+
10
+ @pytest.fixture(scope="function")
11
+ def bec_motor_map(qtbot, mocked_client):
12
+ widget = BECMotorMap(client=mocked_client, gui_id="BECMotorMap_test")
13
+ # qtbot.addWidget(widget)
14
+ # qtbot.waitExposed(widget)
15
+ yield widget
16
+
17
+
18
+ def test_motor_map_init(bec_motor_map):
19
+ default_config = MotorMapConfig(widget_class="BECMotorMap", gui_id="BECMotorMap_test")
20
+
21
+ assert bec_motor_map.config == default_config
22
+
23
+
24
+ def test_motor_map_change_motors(bec_motor_map):
25
+ bec_motor_map.change_motors("samx", "samy")
26
+
27
+ assert bec_motor_map.config.signals.x == SignalData(name="samx", entry="samx", limits=[-10, 10])
28
+ assert bec_motor_map.config.signals.y == SignalData(name="samy", entry="samy", limits=[-5, 5])
29
+
30
+
31
+ def test_motor_map_get_limits(bec_motor_map):
32
+ expected_limits = {
33
+ "samx": [-10, 10],
34
+ "samy": [-5, 5],
35
+ }
36
+
37
+ for motor_name, expected_limit in expected_limits.items():
38
+ actual_limit = bec_motor_map._get_motor_limit(motor_name)
39
+ assert actual_limit == expected_limit
40
+
41
+
42
+ def test_motor_map_get_init_position(bec_motor_map):
43
+ bec_motor_map.set_precision(2)
44
+
45
+ motor_map_dev = bec_motor_map.client.device_manager.devices
46
+
47
+ expected_positions = {
48
+ ("samx", "samx"): motor_map_dev["samx"].read()["samx"]["value"],
49
+ ("samy", "samy"): motor_map_dev["samy"].read()["samy"]["value"],
50
+ ("aptrx", "aptrx"): motor_map_dev["aptrx"].read()["aptrx"]["value"],
51
+ ("aptry", "aptry"): motor_map_dev["aptry"].read()["aptry"]["value"],
52
+ }
53
+
54
+ for (motor_name, entry), expected_position in expected_positions.items():
55
+ actual_position = bec_motor_map._get_motor_init_position(motor_name, entry, 2)
56
+ assert actual_position == expected_position
57
+
58
+
59
+ def test_motor_movement_updates_position_and_database(bec_motor_map):
60
+ motor_map_dev = bec_motor_map.client.device_manager.devices
61
+
62
+ init_positions = {
63
+ "samx": [motor_map_dev["samx"].read()["samx"]["value"]],
64
+ "samy": [motor_map_dev["samy"].read()["samy"]["value"]],
65
+ }
66
+
67
+ bec_motor_map.change_motors("samx", "samy")
68
+
69
+ assert bec_motor_map.database_buffer["x"] == init_positions["samx"]
70
+ assert bec_motor_map.database_buffer["y"] == init_positions["samy"]
71
+
72
+ # Simulate motor movement for 'samx' only
73
+ new_position_samx = 4.0
74
+ bec_motor_map.on_device_readback({"signals": {"samx": {"value": new_position_samx}}})
75
+
76
+ init_positions["samx"].append(new_position_samx)
77
+ init_positions["samy"].append(init_positions["samy"][-1])
78
+ # Verify database update for 'samx'
79
+ assert bec_motor_map.database_buffer["x"] == init_positions["samx"]
80
+
81
+ # Verify 'samy' retains its last known position
82
+ assert bec_motor_map.database_buffer["y"] == init_positions["samy"]
83
+
84
+
85
+ def test_scatter_plot_rendering(bec_motor_map):
86
+ motor_map_dev = bec_motor_map.client.device_manager.devices
87
+
88
+ init_positions = {
89
+ "samx": [motor_map_dev["samx"].read()["samx"]["value"]],
90
+ "samy": [motor_map_dev["samy"].read()["samy"]["value"]],
91
+ }
92
+
93
+ bec_motor_map.change_motors("samx", "samy")
94
+
95
+ # Simulate motor movement for 'samx' only
96
+ new_position_samx = 4.0
97
+ bec_motor_map.on_device_readback({"signals": {"samx": {"value": new_position_samx}}})
98
+ bec_motor_map._update_plot()
99
+
100
+ # Get the scatter plot item
101
+ scatter_plot_item = bec_motor_map.plot_components["scatter"]
102
+
103
+ # Check the scatter plot item properties
104
+ assert len(scatter_plot_item.data) > 0, "Scatter plot data is empty"
105
+ x_data = scatter_plot_item.data["x"]
106
+ y_data = scatter_plot_item.data["y"]
107
+ assert x_data[-1] == new_position_samx, "Scatter plot X data not updated correctly"
108
+ assert (
109
+ y_data[-1] == init_positions["samy"][-1]
110
+ ), "Scatter plot Y data should retain last known position"
111
+
112
+
113
+ def test_plot_visualization_consistency(bec_motor_map):
114
+ bec_motor_map.change_motors("samx", "samy")
115
+ # Simulate updating the plot with new data
116
+ bec_motor_map.on_device_readback({"signals": {"samx": {"value": 5}}})
117
+ bec_motor_map.on_device_readback({"signals": {"samy": {"value": 9}}})
118
+ bec_motor_map._update_plot()
119
+
120
+ scatter_plot_item = bec_motor_map.plot_components["scatter"]
121
+
122
+ # Check if the scatter plot reflects the new data correctly
123
+ assert (
124
+ scatter_plot_item.data["x"][-1] == 5 and scatter_plot_item.data["y"][-1] == 9
125
+ ), "Plot not updated correctly with new data"
@@ -8,6 +8,8 @@ from qtpy.QtWidgets import QTableWidgetItem, QTabWidget
8
8
 
9
9
  from bec_widgets.widgets.monitor.config_dialog import ConfigDialog
10
10
 
11
+ from .client_mocks import mocked_client
12
+
11
13
 
12
14
  def load_test_config(config_name):
13
15
  """Helper function to load config from yaml file."""
@@ -17,69 +19,6 @@ def load_test_config(config_name):
17
19
  return config
18
20
 
19
21
 
20
- class FakeDevice:
21
- """Fake minimal positioner class for testing."""
22
-
23
- def __init__(self, name, enabled=True):
24
- self.name = name
25
- self.enabled = enabled
26
- self.signals = {self.name: {"value": 1.0}}
27
- self.description = {self.name: {"source": self.name}}
28
-
29
- def __contains__(self, item):
30
- return item == self.name
31
-
32
- @property
33
- def _hints(self):
34
- return [self.name]
35
-
36
- def set_value(self, fake_value: float = 1.0) -> None:
37
- """
38
- Setup fake value for device readout
39
- Args:
40
- fake_value(float): Desired fake value
41
- """
42
- self.signals[self.name]["value"] = fake_value
43
-
44
- def describe(self) -> dict:
45
- """
46
- Get the description of the device
47
- Returns:
48
- dict: Description of the device
49
- """
50
- return self.description
51
-
52
-
53
- def get_mocked_device(device_name: str):
54
- """
55
- Helper function to mock the devices
56
- Args:
57
- device_name(str): Name of the device to mock
58
- """
59
- return FakeDevice(name=device_name, enabled=True)
60
-
61
-
62
- @pytest.fixture(scope="function")
63
- def mocked_client():
64
- # Create a dictionary of mocked devices
65
- device_names = ["samx", "gauss_bpm", "gauss_adc1", "gauss_adc2", "gauss_adc3", "bpm4i"]
66
- mocked_devices = {name: get_mocked_device(name) for name in device_names}
67
-
68
- # Create a MagicMock object
69
- client = MagicMock()
70
-
71
- # Mock the device_manager.devices attribute
72
- client.device_manager.devices = MagicMock()
73
- client.device_manager.devices.__getitem__.side_effect = lambda x: mocked_devices.get(x)
74
- client.device_manager.devices.__contains__.side_effect = lambda x: x in mocked_devices
75
-
76
- # Set each device as an attribute of the mock
77
- for name, device in mocked_devices.items():
78
- setattr(client.device_manager.devices, name, device)
79
-
80
- return client
81
-
82
-
83
22
  @pytest.fixture(scope="function")
84
23
  def config_dialog(qtbot, mocked_client):
85
24
  client = mocked_client