bec-widgets 0.44.4__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.
Files changed (67) hide show
  1. bec_widgets/cli/client.py +105 -5
  2. bec_widgets/cli/client_utils.py +4 -4
  3. bec_widgets/cli/generate_cli.py +6 -4
  4. bec_widgets/cli/server.py +2 -3
  5. bec_widgets/examples/__init__.py +1 -1
  6. bec_widgets/examples/eiger_plot/eiger_plot.py +3 -11
  7. bec_widgets/examples/mca_readout/mca_plot.py +21 -24
  8. bec_widgets/examples/mca_readout/mca_sim.py +7 -12
  9. bec_widgets/examples/motor_movement/__init__.py +1 -1
  10. bec_widgets/examples/motor_movement/motor_control_compilations.py +3 -8
  11. bec_widgets/examples/motor_movement/motor_controller.ui +1 -1
  12. bec_widgets/examples/motor_movement/motor_example.py +13 -14
  13. bec_widgets/examples/stream_plot/stream_plot.py +5 -4
  14. bec_widgets/utils/__init__.py +5 -5
  15. bec_widgets/utils/bec_connector.py +1 -1
  16. bec_widgets/utils/bec_dispatcher.py +1 -2
  17. bec_widgets/utils/bec_table.py +1 -1
  18. bec_widgets/utils/crosshair.py +2 -1
  19. bec_widgets/utils/validator_delegate.py +1 -1
  20. bec_widgets/utils/widget_io.py +6 -6
  21. bec_widgets/utils/yaml_dialog.py +1 -0
  22. bec_widgets/validation/monitor_config_validator.py +6 -6
  23. bec_widgets/widgets/__init__.py +7 -8
  24. bec_widgets/widgets/editor/editor.py +6 -14
  25. bec_widgets/widgets/figure/__init__.py +1 -1
  26. bec_widgets/widgets/figure/figure.py +74 -4
  27. bec_widgets/widgets/monitor/__init__.py +0 -1
  28. bec_widgets/widgets/monitor/config_dialog.py +7 -8
  29. bec_widgets/widgets/monitor/monitor.py +16 -26
  30. bec_widgets/widgets/monitor_scatter_2D/monitor_scatter_2D.py +11 -19
  31. bec_widgets/widgets/motor_control/__init__.py +2 -2
  32. bec_widgets/widgets/motor_control/motor_control.py +11 -10
  33. bec_widgets/widgets/motor_map/motor_map.py +2 -3
  34. bec_widgets/widgets/plots/__init__.py +4 -3
  35. bec_widgets/widgets/plots/image.py +7 -6
  36. bec_widgets/widgets/plots/motor_map.py +423 -0
  37. bec_widgets/widgets/plots/plot_base.py +1 -2
  38. bec_widgets/widgets/plots/waveform1d.py +24 -23
  39. bec_widgets/widgets/scan_control/scan_control.py +12 -12
  40. bec_widgets/widgets/toolbar/toolbar.py +2 -4
  41. {bec_widgets-0.44.4.dist-info → bec_widgets-0.45.0.dist-info}/METADATA +2 -1
  42. bec_widgets-0.45.0.dist-info/RECORD +98 -0
  43. tests/client_mocks.py +76 -30
  44. tests/conftest.py +2 -1
  45. tests/test_bec_connector.py +2 -1
  46. tests/test_bec_dispatcher.py +3 -4
  47. tests/test_bec_figure.py +14 -2
  48. tests/test_bec_monitor.py +7 -76
  49. tests/test_bec_monitor_scatter2D.py +8 -32
  50. tests/test_bec_motor_map.py +125 -0
  51. tests/test_config_dialog.py +4 -66
  52. tests/test_editor.py +2 -4
  53. tests/test_eiger_plot.py +2 -0
  54. tests/test_generate_cli_client.py +4 -2
  55. tests/test_motor_control.py +25 -92
  56. tests/test_motor_map.py +10 -66
  57. tests/test_plot_base.py +1 -0
  58. tests/test_scan_control.py +1 -1
  59. tests/test_stream_plot.py +2 -2
  60. tests/test_validator_errors.py +5 -4
  61. tests/test_waveform1d.py +31 -12
  62. tests/test_widget_io.py +1 -8
  63. tests/test_yaml_dialog.py +2 -1
  64. bec_widgets-0.44.4.dist-info/RECORD +0 -96
  65. {bec_widgets-0.44.4.dist-info → bec_widgets-0.45.0.dist-info}/LICENSE +0 -0
  66. {bec_widgets-0.44.4.dist-info → bec_widgets-0.45.0.dist-info}/WHEEL +0 -0
  67. {bec_widgets-0.44.4.dist-info → bec_widgets-0.45.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,98 @@
1
+ bec_widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ bec_widgets/cli/__init__.py,sha256=yFyAmDteCcndbReunhnrfDib6JujQ7-BKWeVvuU0Ylw,30
3
+ bec_widgets/cli/client.py,sha256=J3yvhteGky9XHXMoyN0am75uAE4qeo7Oj_dZbdtMHRM,37300
4
+ bec_widgets/cli/client_utils.py,sha256=8tVOmAMo5x0tUywTfivvCt8n0tJoT5WzoCa3GtsZ-qE,9694
5
+ bec_widgets/cli/generate_cli.py,sha256=QqTacqaG3zYi-Zmun62j-4Tv-Az5rDbvFXA2ESFjWZ0,4010
6
+ bec_widgets/cli/server.py,sha256=4552dbbdJlalIllQGJFR3AsN0iLl5Qs23BAeqQiof0s,4721
7
+ bec_widgets/examples/__init__.py,sha256=WWQ0cu7m8sA4Ehy-DWdTIqSISjaHsbxhsNmNrMnhDZU,202
8
+ bec_widgets/examples/eiger_plot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ bec_widgets/examples/eiger_plot/eiger_plot.py,sha256=Uxl2Usf8jEzaX7AT8zVqa1x8ZIEgI1HmazSlb-tRFWE,10359
10
+ bec_widgets/examples/eiger_plot/eiger_plot.ui,sha256=grHfnO3OG_lelJhdRsnA0badCvRdDunPrIMIyNQ5N-w,5809
11
+ bec_widgets/examples/mca_readout/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ bec_widgets/examples/mca_readout/mca_plot.py,sha256=do7mSK_nzHtojRiMi8JoN_Rckg9yfjYYWz2S_Nl3xbE,5079
13
+ bec_widgets/examples/mca_readout/mca_sim.py,sha256=Dv_yYJ-uCmfMTBX0l-yVZqn-UHpfo8L3jNZ-JOPtzHk,867
14
+ bec_widgets/examples/motor_movement/__init__.py,sha256=LzPJkxLAxOsZCbXR-fRCPmeYobp7Yqds6tDxW4W1gSw,214
15
+ bec_widgets/examples/motor_movement/config_example.yaml,sha256=YT8Bl3ViTaP03cX9X8kIgWnkoxaQOFqS-oRrclGS2hc,356
16
+ bec_widgets/examples/motor_movement/csax_bec_config.yaml,sha256=cqhzwfIhFgHfCt90oVW3no8aVlWg7V42t4iiJCnydSw,146
17
+ bec_widgets/examples/motor_movement/csaxs_config.yaml,sha256=poNndp-1ptdnriIJ_RTnbAimFlOJ45VdxfkclGEQlgc,344
18
+ bec_widgets/examples/motor_movement/motor_control_compilations.py,sha256=HIDauBIkNmKB8oCuGy_toNkIYxsdBCEniK2zj6vZFVg,8979
19
+ bec_widgets/examples/motor_movement/motor_controller.ui,sha256=83XX6NGILwntoUIghvzWnMuGf80O8khK3SduVKTAEFM,29105
20
+ bec_widgets/examples/motor_movement/motor_example.py,sha256=_0S_VnksFhwb3WM3dQuI9BUrn8UYzXJg5q6ayPCsaoI,53414
21
+ bec_widgets/examples/stream_plot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ bec_widgets/examples/stream_plot/line_plot.ui,sha256=rgNfhOXu1AcWF0P6wnOlmJKDjS-VIoduVrREvmzJQR8,4626
23
+ bec_widgets/examples/stream_plot/stream_plot.py,sha256=ZzvJm4T--oxmAGj4IeDWsNuBrWq_MyY04vI_OFJYs4A,12184
24
+ bec_widgets/simulations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ bec_widgets/utils/__init__.py,sha256=-szVMxtb6Aoz5_e6Py652ExbGnmSVjd0w6ULBvumguk,353
26
+ bec_widgets/utils/bec_connector.py,sha256=h4GGoaPSsdBolG5n8uCVWILHTe46NckTQqdBHBPlRaE,3652
27
+ bec_widgets/utils/bec_dispatcher.py,sha256=nzocqIcVzJpWP5hTnAx0l-M6Wm7pU2sfW5KmQrMVbu0,7365
28
+ bec_widgets/utils/bec_table.py,sha256=Xy5qM343K8EvEpB4g_129b63yo1wdEvEY3wqxB_p_Iw,716
29
+ bec_widgets/utils/colors.py,sha256=JsLxzkxbw-I8GIuvnIKyiM83n0edhyMG2Fa4Ffm62ww,2392
30
+ bec_widgets/utils/crosshair.py,sha256=5gG4G6jjtp6Bd1Y5EySHP2EkmtR4mYdxLCgVtx9fokE,9406
31
+ bec_widgets/utils/ctrl_c.py,sha256=NMJlPDZcuqMUGykyhuZY5Ibed4yRI1K_uh16z2MmlXQ,1198
32
+ bec_widgets/utils/entry_validator.py,sha256=nPdu87Ry9Vka2QVbOLW4YbLJwEWpRYypNl7hlXaMaHM,614
33
+ bec_widgets/utils/rpc_decorator.py,sha256=pIvtqySQLnuS7l2Ti_UAe4WX7CRivZnsE5ZdKAihxh0,479
34
+ bec_widgets/utils/validator_delegate.py,sha256=Emj1WF6W8Ke1ruBWUfmHdVJpmOSPezuOt4zvQTay_44,442
35
+ bec_widgets/utils/widget_io.py,sha256=JKl508VnqQSxcaHqKaoBQ1TWSOm3pXhxQGx7iF_pRA0,10875
36
+ bec_widgets/utils/yaml_dialog.py,sha256=soZI8BOjlqYGfYDga70MEvkxJTsktq4y7B3uog2cSik,1851
37
+ bec_widgets/validation/__init__.py,sha256=ismd1bU5FhFb0zFPwNKuq7oT48G4Y2GfaMZOdNKUtGk,132
38
+ bec_widgets/validation/monitor_config_validator.py,sha256=M9p8K_nvxicnqJB4X7j90R377WHYVH4wMCtSXsRI51M,8150
39
+ bec_widgets/widgets/__init__.py,sha256=QNCuDcg0o3uvGbf74qXeFjEsgIzOeEcd1hoWabJ4tKQ,437
40
+ bec_widgets/widgets/editor/__init__.py,sha256=5mBdFYi_IpygCz81kbLEZUWhd1b6oqiO3nASejuV_ug,30
41
+ bec_widgets/widgets/editor/editor.py,sha256=pIIYLPqqqhXqT11Xj10cyGEiy-ieNGE4ZujN5lf0e68,15110
42
+ bec_widgets/widgets/figure/__init__.py,sha256=3hGx_KOV7QHCYAV06aNuUgKq4QIYCjUTad-DrwkUaBM,44
43
+ bec_widgets/widgets/figure/figure.py,sha256=-3SJu93gE73ttrfxD9NGPRmgvaZjH0jOX_7ovhrHXck,32454
44
+ bec_widgets/widgets/figure/figure_debug_minimal.ui,sha256=GodXBvBvs5QAUsHbo3pcxR4o51Tvce4DTqpTluk3hOs,742
45
+ bec_widgets/widgets/monitor/__init__.py,sha256=afXuZcBOxNAuYdCkIQXX5J60R5A3Q_86lNEW2vpFtPI,32
46
+ bec_widgets/widgets/monitor/config_dialog.py,sha256=Z1a4WRIVlfEGdwC-QG25kba2EHCZWi5J843tBVZlWiI,20275
47
+ bec_widgets/widgets/monitor/config_dialog.ui,sha256=ISMcF7CLTAMXhfZh2Yv5yezzAjMtb9fxY1pmX4B_jCg,5932
48
+ bec_widgets/widgets/monitor/monitor.py,sha256=ET5O48kqRlwd8_jCMCrp8QwnL8LvoNgb5nQmVw36pKg,30219
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
+ bec_widgets/widgets/motor_control/__init__.py,sha256=_4-G9AKcEnOyS6qVE26meRw3grEohNeMzACNMU5Sig0,153
53
+ bec_widgets/widgets/motor_control/motor_control.py,sha256=sfH_uGes4B3mOFPP9BYVMtqMSCFD5fWJNQwNxV06288,42458
54
+ bec_widgets/widgets/motor_control/motor_control_absolute.ui,sha256=nR3p6oevAkIBTLW5wM_zYOVWsCAUgeMZdRm10Q77COE,4126
55
+ bec_widgets/widgets/motor_control/motor_control_relative.ui,sha256=PulNJNiws7TRAxHM2snNyvlmQ0tRShdArHmtaC85h4U,8684
56
+ bec_widgets/widgets/motor_control/motor_control_selection.ui,sha256=vXXpvNWuL6xyHhW7Lx1zmVFX-95Z5AXGlhKQD2HmM1A,1779
57
+ bec_widgets/widgets/motor_control/motor_control_table.ui,sha256=t6aRKiSmutMfp0AyupavbCs0cal-FANEnlKQiPzC9PQ,2792
58
+ bec_widgets/widgets/motor_map/__init__.py,sha256=K3c-3A_LbxK0UJ0_bV3opL-wGLTwBLendsJXsg8GAqE,32
59
+ bec_widgets/widgets/motor_map/motor_map.py,sha256=A7VOs8RAiXaLZF4_oxOYJX5BwMhRvir0k0u5eEPFtLc,21909
60
+ bec_widgets/widgets/plots/__init__.py,sha256=ncGc6re47T9Bq75RQrPrk2rX2V89QZWRFOwZjmvkQPw,241
61
+ bec_widgets/widgets/plots/image.py,sha256=Ty-8etIYBQl1MJLcuwCtlLUcAuXbxBq4RLDikbTL34c,31748
62
+ bec_widgets/widgets/plots/motor_map.py,sha256=yBqVvLgEQhiIpQyvmsoTUToEyLblVmS2sTST7N8hNQA,15115
63
+ bec_widgets/widgets/plots/plot_base.py,sha256=tF0oamgCVIXFrfD8jnMVTltkzYLLSQ0Dq9HAG57Ji3E,8446
64
+ bec_widgets/widgets/plots/waveform1d.py,sha256=RbI5dQlbAaC4A1WuQLS1DWpk5UjeELpaUEvHkFvZAbA,25631
65
+ bec_widgets/widgets/scan_control/__init__.py,sha256=IOfHl15vxb_uC6KN62-PeUzbBha_vQyqkkXbJ2HU674,38
66
+ bec_widgets/widgets/scan_control/scan_control.py,sha256=tbO9tbVynRvs4VCxTZ4ZFBDTVAojIr-zkl70vuHbWgw,17116
67
+ bec_widgets/widgets/toolbar/__init__.py,sha256=d-TP4_cr_VbpwreMM4ePnfZ5YXsEPQ45ibEf75nuGoE,36
68
+ bec_widgets/widgets/toolbar/toolbar.py,sha256=sxz7rbc8XNPS6n2WMObF4-2PqdYfPxVtsOZEGV6mqa0,5124
69
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
+ tests/client_mocks.py,sha256=925yW3vFnS8h6ym_4kpgX299WGVeObkGneuiXNWRE7s,3596
71
+ tests/conftest.py,sha256=wMFVnerrLZjpi-OnPf7Ugah_DCb51QD1WWcbR4e_M6k,1055
72
+ tests/test_bec_connector.py,sha256=f2XXGGw3NoZLIUrDuZuEWwF_ttOYmmquCgUrV5XkIOY,1951
73
+ tests/test_bec_dispatcher.py,sha256=vWQGdUyMvssrOyiVxdsOpErOh4awX_NSQWvKSBoRfAI,9223
74
+ tests/test_bec_figure.py,sha256=-2KYedk32pCsXNmAN0OFUrQT101YV3rrSL0ET3KXjjA,7464
75
+ tests/test_bec_monitor.py,sha256=mN7gBY7oXY6j65zzihpy8r-FvwVoCQlie3F6SoVq0mo,7042
76
+ tests/test_bec_monitor_scatter2D.py,sha256=UYs0pe9uBX7GpFVkhxw-Hx_MwTz7NKMtWIBqONTZ1JI,5797
77
+ tests/test_bec_motor_map.py,sha256=5D-xPQQ731Q6wsEzAmqJqoy_FpHtlK1siwqql8sJ0p0,4613
78
+ tests/test_config_dialog.py,sha256=5uNGcpvrx8qDdMwFCTXr8HMzFZF4rFi-ZHoDpMxGMf8,6955
79
+ tests/test_crosshair.py,sha256=d7fX-ymboZPALNqqiAj86PZ96llmGZ_3jf0yjVP0S94,5039
80
+ tests/test_editor.py,sha256=TED5k1xFJHRZ4KDAg2VxSRu_hMJnra-lbAmVwsDicsM,6784
81
+ tests/test_eiger_plot.py,sha256=bWnKBQid0YcLMQeBLy6ojb4ZpwTG-rFVT0kMg9Y08p8,4427
82
+ tests/test_generate_cli_client.py,sha256=BdpTZMNUFOBJa2e-rme9AJUoXfueYyLiUCOpGi3SNvc,2400
83
+ tests/test_motor_control.py,sha256=jdTG35z3jOL9XCAIDNIGfdv60vcwGLHa3KJjKqJkoZw,20322
84
+ tests/test_motor_map.py,sha256=UEjmtIYI2mxq9BUeopqoqNNy7UiPJEts9h45ufsFcrA,5979
85
+ tests/test_plot_base.py,sha256=bOdlgAxh9oKk5PwiQ_MSFmzr44uJ61Tlg242RCIhl5c,2610
86
+ tests/test_scan_control.py,sha256=e6_YpyxcayK35jXBSfxinGNL_8G-jcrWap25eg3z4QI,7560
87
+ tests/test_stream_plot.py,sha256=LNCYIj9CafremGaz-DwDktCRJRrjgfOdVewCUwwZE5s,5843
88
+ tests/test_validator_errors.py,sha256=NFxyv0TIOXeZKZRRUBfVQ7bpunwY4KkG95yTUdQmvns,3532
89
+ tests/test_waveform1d.py,sha256=GzlQTAjbg35mMKhopLCiHy3fIx0ld6WTr46ZuMTeSTE,13644
90
+ tests/test_widget_io.py,sha256=FeL3ZYSBQnRt6jxj8VGYw1cmcicRQyHKleahw7XIyR0,3475
91
+ tests/test_yaml_dialog.py,sha256=HNrqferkdg02-9ieOhhI2mr2Qvt7GrYgXmQ061YCTbg,5794
92
+ tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
+ tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
94
+ bec_widgets-0.45.0.dist-info/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
95
+ bec_widgets-0.45.0.dist-info/METADATA,sha256=9EPs7EAqxS-MNM5INN1K7kdqlawrd_oeyHwDhotp5wI,3714
96
+ bec_widgets-0.45.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
97
+ bec_widgets-0.45.0.dist-info/top_level.txt,sha256=EXCwhJYmXmd1DjYYL3hrGsddX-97IwYSiIHrf27FFVk,18
98
+ bec_widgets-0.45.0.dist-info/RECORD,,
tests/client_mocks.py CHANGED
@@ -1,7 +1,9 @@
1
1
  # pylint: disable = no-name-in-module,missing-class-docstring, missing-module-docstring
2
+ from unittest.mock import MagicMock, patch
2
3
 
3
4
  import pytest
4
- from unittest.mock import MagicMock
5
+ from bec_lib.device import Positioner
6
+ from bec_lib.devicemanager import DeviceContainer
5
7
 
6
8
 
7
9
  class FakeDevice:
@@ -11,7 +13,7 @@ class FakeDevice:
11
13
  self.name = name
12
14
  self.enabled = enabled
13
15
  self.signals = {self.name: {"value": 1.0}}
14
- self.description = {self.name: {"source": self.name}}
16
+ self.description = {self.name: {"source": self.name, "dtype": "number", "shape": []}}
15
17
 
16
18
  def __contains__(self, item):
17
19
  return item == self.name
@@ -37,41 +39,85 @@ class FakeDevice:
37
39
  return self.description
38
40
 
39
41
 
40
- def get_mocked_device(device_name: str):
41
- """
42
- Helper function to mock the devices
43
- Args:
44
- device_name(str): Name of the device to mock
45
- """
46
- 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
+ ]
47
93
 
48
94
 
49
95
  @pytest.fixture(scope="function")
50
96
  def mocked_client():
51
- # Create a dictionary of mocked devices
52
- device_names = [
53
- "samx",
54
- "samy",
55
- "gauss_bpm",
56
- "gauss_adc1",
57
- "gauss_adc2",
58
- "gauss_adc3",
59
- "bpm4i",
60
- "bpm3a",
61
- "bpm3i",
62
- ]
63
- mocked_devices = {name: get_mocked_device(name) for name in device_names}
64
-
65
97
  # Create a MagicMock object
66
98
  client = MagicMock()
67
99
 
68
100
  # Mock the device_manager.devices attribute
69
- client.device_manager.devices = MagicMock()
70
- client.device_manager.devices.__getitem__.side_effect = lambda x: mocked_devices.get(x)
71
- 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
72
116
 
73
- # Set each device as an attribute of the mock
74
- for name, device in mocked_devices.items():
75
- 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)
76
121
 
77
- return client
122
+ with patch("builtins.isinstance", new=isinstance_mock):
123
+ yield client
tests/conftest.py CHANGED
@@ -1,7 +1,8 @@
1
- import pytest
2
1
  import threading
3
2
 
3
+ import pytest
4
4
  from bec_lib.bec_service import BECService
5
+
5
6
  from bec_widgets.utils import bec_dispatcher as bec_dispatcher_module
6
7
 
7
8
 
@@ -1,9 +1,10 @@
1
1
  # pylint: disable = no-name-in-module,missing-class-docstring, missing-module-docstring
2
2
  import pytest
3
3
 
4
- from .client_mocks import mocked_client
5
4
  from bec_widgets.utils import BECConnector, ConnectionConfig
6
5
 
6
+ from .client_mocks import mocked_client
7
+
7
8
 
8
9
  @pytest.fixture
9
10
  def bec_connector(mocked_client):
@@ -2,11 +2,10 @@
2
2
  from unittest.mock import Mock
3
3
 
4
4
  import pytest
5
- from bec_lib.messages import ScanMessage
6
5
  from bec_lib.connector import MessageObject
6
+ from bec_lib.messages import ScanMessage
7
7
 
8
-
9
- msg = MessageObject(topic="", value=ScanMessage(point_id=0, scanID=0, data={}))
8
+ msg = MessageObject(topic="", value=ScanMessage(point_id=0, scan_id=0, data={}))
10
9
 
11
10
 
12
11
  @pytest.fixture(name="consumer")
@@ -206,7 +205,7 @@ def test_connect_one_slot_multiple_topics_single_callback(bec_dispatcher, consum
206
205
  # Simulate messages being published on each topic
207
206
  for topic in topics:
208
207
  msg_with_topic = MessageObject(
209
- topic=topic, value=ScanMessage(point_id=0, scanID=0, data={})
208
+ topic=topic, value=ScanMessage(point_id=0, scan_id=0, data={})
210
209
  )
211
210
  consumer.register.call_args.kwargs["cb"](msg_with_topic)
212
211
 
tests/test_bec_figure.py CHANGED
@@ -1,12 +1,14 @@
1
1
  # pylint: disable=missing-function-docstring, missing-module-docstring, unused-import
2
2
  import os
3
+ from unittest.mock import MagicMock
3
4
 
4
5
  import numpy as np
5
6
  import pytest
6
- from unittest.mock import MagicMock
7
+
8
+ from bec_widgets.widgets import BECFigure, BECMotorMap, BECWaveform1D
9
+ from bec_widgets.widgets.plots import BECImageShow
7
10
 
8
11
  from .client_mocks import mocked_client
9
- from bec_widgets.widgets import BECFigure
10
12
 
11
13
 
12
14
  @pytest.fixture
@@ -65,6 +67,16 @@ def test_bec_figure_add_remove_plot(bec_figure):
65
67
  assert bec_figure._widgets["widget_2"].config.widget_class == "BECWaveform1D"
66
68
 
67
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__ == BECWaveform1D
76
+ assert im.__class__ == BECImageShow
77
+ assert motor_map.__class__ == BECMotorMap
78
+
79
+
68
80
  def test_access_widgets_access_errors(bec_figure):
69
81
  bec_figure.add_plot(row=0, col=0)
70
82
 
tests/test_bec_monitor.py CHANGED
@@ -1,12 +1,14 @@
1
1
  # pylint: disable = no-name-in-module,missing-class-docstring, missing-module-docstring
2
2
  import os
3
- import yaml
3
+ from unittest.mock import MagicMock
4
4
 
5
5
  import pytest
6
- from unittest.mock import MagicMock
6
+ 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()
@@ -126,12 +65,7 @@ def test_on_config_update(monitor, config_initial, config_update):
126
65
  @pytest.mark.parametrize(
127
66
  "config_name, expected_num_columns, expected_plot_names, expected_coordinates",
128
67
  [
129
- (
130
- "config_device",
131
- 1,
132
- ["BPM4i plots vs samx", "Gauss plots vs samx"],
133
- [(0, 0), (1, 0)],
134
- ),
68
+ ("config_device", 1, ["BPM4i plots vs samx", "Gauss plots vs samx"], [(0, 0), (1, 0)]),
135
69
  (
136
70
  "config_scan",
137
71
  3,
@@ -186,7 +120,7 @@ msg_1 = {
186
120
  "gauss_adc1": {"gauss_adc1": {"value": 8}},
187
121
  "gauss_adc2": {"gauss_adc2": {"value": 9}},
188
122
  },
189
- "scanID": 1,
123
+ "scan_id": 1,
190
124
  }
191
125
  metadata_grid = {"scan_name": "grid_scan"}
192
126
  metadata_line = {"scan_name": "line_scan"}
@@ -195,7 +129,7 @@ metadata_line = {"scan_name": "line_scan"}
195
129
  @pytest.mark.parametrize(
196
130
  "config_name, msg, metadata, expected_data",
197
131
  [
198
- # case: msg does not have 'scanID'
132
+ # case: msg does not have 'scan_id'
199
133
  (
200
134
  "config_device",
201
135
  {"data": {}},
@@ -271,9 +205,6 @@ def test_on_scan_segment(monitor, config_name, msg, metadata, expected_data):
271
205
  config = load_test_config(config_name)
272
206
  monitor.on_config_update(config)
273
207
 
274
- # Get hints
275
- monitor.dev.__getitem__.side_effect = mock_getitem
276
-
277
208
  # Mock scan_storage.find_scan_by_ID
278
209
  mock_scan_data = MagicMock()
279
210
  mock_scan_data.data = {
@@ -1,17 +1,14 @@
1
1
  # pylint: disable=missing-module-docstring, missing-function-docstring
2
2
  from collections import defaultdict
3
+ from unittest.mock import MagicMock
3
4
 
4
5
  import pytest
5
- from unittest.mock import MagicMock
6
6
  from qtpy import QtGui
7
7
 
8
8
  from bec_widgets.widgets import BECMonitor2DScatter
9
9
 
10
10
  CONFIG_DEFAULT = {
11
- "plot_settings": {
12
- "colormap": "CET-L4",
13
- "num_columns": 1,
14
- },
11
+ "plot_settings": {"colormap": "CET-L4", "num_columns": 1},
15
12
  "waveform2D": [
16
13
  {
17
14
  "plot_name": "Waveform 2D Scatter (1)",
@@ -37,10 +34,7 @@ CONFIG_DEFAULT = {
37
34
  }
38
35
 
39
36
  CONFIG_ONE_PLOT = {
40
- "plot_settings": {
41
- "colormap": "CET-L4",
42
- "num_columns": 1,
43
- },
37
+ "plot_settings": {"colormap": "CET-L4", "num_columns": 1},
44
38
  "waveform2D": [
45
39
  {
46
40
  "plot_name": "Waveform 2D Scatter (1)",
@@ -51,7 +45,7 @@ CONFIG_ONE_PLOT = {
51
45
  "y": [{"name": "aptry", "entry": "aptry"}],
52
46
  "z": [{"name": "gauss_bpm", "entry": "gauss_bpm"}],
53
47
  },
54
- },
48
+ }
55
49
  ],
56
50
  }
57
51
 
@@ -65,13 +59,7 @@ def monitor_2Dscatter(qtbot):
65
59
  yield widget
66
60
 
67
61
 
68
- @pytest.mark.parametrize(
69
- "config, number_of_plots",
70
- [
71
- (CONFIG_DEFAULT, 2),
72
- (CONFIG_ONE_PLOT, 1),
73
- ],
74
- )
62
+ @pytest.mark.parametrize("config, number_of_plots", [(CONFIG_DEFAULT, 2), (CONFIG_ONE_PLOT, 1)])
75
63
  def test_initialization(monitor_2Dscatter, config, number_of_plots):
76
64
  config_load = config
77
65
  monitor_2Dscatter.on_config_update(config_load)
@@ -81,13 +69,7 @@ def test_initialization(monitor_2Dscatter, config, number_of_plots):
81
69
  assert len(monitor_2Dscatter.plot_data) == number_of_plots
82
70
 
83
71
 
84
- @pytest.mark.parametrize(
85
- "config ",
86
- [
87
- (CONFIG_DEFAULT),
88
- (CONFIG_ONE_PLOT),
89
- ],
90
- )
72
+ @pytest.mark.parametrize("config ", [(CONFIG_DEFAULT), (CONFIG_ONE_PLOT)])
91
73
  def test_database_initialization(monitor_2Dscatter, config):
92
74
  monitor_2Dscatter.on_config_update(config)
93
75
  # Check if the database is a defaultdict
@@ -108,13 +90,7 @@ def test_database_initialization(monitor_2Dscatter, config):
108
90
  assert isinstance(monitor_2Dscatter.database[plot_name][axis][signal_name], list)
109
91
 
110
92
 
111
- @pytest.mark.parametrize(
112
- "config ",
113
- [
114
- (CONFIG_DEFAULT),
115
- (CONFIG_ONE_PLOT),
116
- ],
117
- )
93
+ @pytest.mark.parametrize("config ", [(CONFIG_DEFAULT), (CONFIG_ONE_PLOT)])
118
94
  def test_ui_initialization(monitor_2Dscatter, config):
119
95
  monitor_2Dscatter.on_config_update(config)
120
96
  assert len(monitor_2Dscatter.plots) == len(config["waveform2D"])
@@ -133,7 +109,7 @@ def simulate_scan_data(monitor, x_value, y_value, z_value):
133
109
  "samy": {"samy": {"value": y_value}},
134
110
  "gauss_bpm": {"gauss_bpm": {"value": z_value}},
135
111
  },
136
- "scanID": 1,
112
+ "scan_id": 1,
137
113
  }
138
114
  monitor.on_scan_segment(msg, {})
139
115
 
@@ -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.waveform1d 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"