bec-widgets 0.50.1__py3-none-any.whl → 0.51.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.
@@ -129,7 +129,15 @@ class BECDispatcher:
129
129
  self._slots[slot].update(set(topics_str))
130
130
 
131
131
  def disconnect_slot(self, slot: Callable, topics: Union[str, list]):
132
- self.client.connector.unregister(topics, cb=slot)
132
+ # find the right slot to disconnect from ;
133
+ # slot callbacks are wrapped in QtThreadSafeCallback objects,
134
+ # but the slot we receive here is the original callable
135
+ for connected_slot in self._slots:
136
+ if connected_slot.cb == slot:
137
+ break
138
+ else:
139
+ return
140
+ self.client.connector.unregister(topics, cb=connected_slot)
133
141
  topics_str, _ = self.client.connector._convert_endpointinfo(topics)
134
142
  self._slots[slot].difference_update(set(topics_str))
135
143
  if not self._slots[slot]:
@@ -0,0 +1,40 @@
1
+ import inspect
2
+
3
+ from bec_lib.plugin_helper import _get_available_plugins
4
+
5
+ from bec_widgets.utils import BECConnector
6
+
7
+
8
+ def get_plugin_widgets() -> dict[str, BECConnector]:
9
+ """
10
+ Get all available widgets from the plugin directory. Widgets are classes that inherit from BECConnector.
11
+ The plugins are provided through python plugins and specified in the respective pyproject.toml file using
12
+ the following key:
13
+
14
+ [project.entry-points."bec.widgets.user_widgets"]
15
+ plugin_widgets = "path.to.plugin.module"
16
+
17
+ e.g.
18
+ [project.entry-points."bec.widgets.user_widgets"]
19
+ plugin_widgets = "pxiii_bec.bec_widgets.widgets"
20
+
21
+ assuming that the widgets module for the package pxiii_bec is located at pxiii_bec/bec_widgets/widgets and
22
+ contains the widgets to be loaded within the pxiii_bec/bec_widgets/widgets/__init__.py file.
23
+
24
+ Returns:
25
+ dict[str, BECConnector]: A dictionary of widget names and their respective classes.
26
+ """
27
+ modules = _get_available_plugins("bec.widgets.user_widgets")
28
+ loaded_plugins = {}
29
+ print(modules)
30
+ for module in modules:
31
+ mods = inspect.getmembers(module, predicate=_filter_plugins)
32
+ for name, mod_cls in mods:
33
+ if name in loaded_plugins:
34
+ print(f"Duplicated widgets plugin {name}.")
35
+ loaded_plugins[name] = mod_cls
36
+ return loaded_plugins
37
+
38
+
39
+ def _filter_plugins(obj):
40
+ return inspect.isclass(obj) and issubclass(obj, BECConnector)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bec-widgets
3
- Version: 0.50.1
3
+ Version: 0.51.0
4
4
  Summary: BEC Widgets
5
5
  Home-page: https://gitlab.psi.ch/bec/bec-widgets
6
6
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec-widgets/issues
@@ -12,8 +12,7 @@ Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
  Requires-Dist: pydantic
14
14
  Requires-Dist: qtconsole
15
- Requires-Dist: PyQt6-Qt6 <=6.6.3
16
- Requires-Dist: PyQt6 <=6.6.3
15
+ Requires-Dist: PyQt6 >=6.7
17
16
  Requires-Dist: jedi
18
17
  Requires-Dist: qtpy
19
18
  Requires-Dist: pyqtgraph
@@ -30,10 +29,11 @@ Requires-Dist: coverage ; extra == 'dev'
30
29
  Requires-Dist: pytest-qt ; extra == 'dev'
31
30
  Requires-Dist: black ; extra == 'dev'
32
31
  Requires-Dist: isort ; extra == 'dev'
32
+ Requires-Dist: fakeredis ; extra == 'dev'
33
33
  Provides-Extra: pyqt5
34
34
  Requires-Dist: PyQt5 >=5.9 ; extra == 'pyqt5'
35
35
  Provides-Extra: pyqt6
36
- Requires-Dist: PyQt6 <=6.6.3 ; extra == 'pyqt6'
36
+ Requires-Dist: PyQt6 >=6.7 ; extra == 'pyqt6'
37
37
 
38
38
  # BEC Widgets
39
39
 
@@ -43,14 +43,14 @@ BEC Widgets is a GUI framework designed for interaction with [BEC (Beamline Expe
43
43
  Use the package manager [pip](https://pip.pypa.io/en/stable/) to install BEC Widgets:
44
44
 
45
45
  ```bash
46
- pip install bec-widgets
46
+ pip install bec_widgets
47
47
  ```
48
48
 
49
49
  For development purposes, you can clone the repository and install the package locally in editable mode:
50
50
 
51
51
  ```bash
52
52
  git clone https://gitlab.psi.ch/bec/bec-widgets
53
- cd bec-widgets
53
+ cd bec_widgets
54
54
  pip install -e .[dev]
55
55
  ```
56
56
 
@@ -59,12 +59,12 @@ BEC Widgets currently supports both PyQt5 and PyQt6. By default, PyQt6 is instal
59
59
  To select a specific Python Qt distribution, install the package with an additional tag:
60
60
 
61
61
  ```bash
62
- pip install bec-widgets[pyqt6]
62
+ pip install bec_widgets[pyqt6]
63
63
  ```
64
64
  or
65
65
 
66
66
  ```bash
67
- pip install bec-widgets[pyqt5]
67
+ pip install bec_widgets[pyqt5]
68
68
  ```
69
69
  ## Documentation
70
70
 
@@ -30,13 +30,14 @@ bec_widgets/examples/stream_plot/stream_plot.py,sha256=vHii1p9JxSyGQ_VcCjnk9SHJ4
30
30
  bec_widgets/simulations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  bec_widgets/utils/__init__.py,sha256=xytx86Yosjkta0PU4rHfoeO7FCPcimS15xjMPQUgIXc,403
32
32
  bec_widgets/utils/bec_connector.py,sha256=U_quQy7p1ISEpTnvwKsnDw5rdCc3jEoATfPVez2K7eI,4867
33
- bec_widgets/utils/bec_dispatcher.py,sha256=krHaItLU_8RLa4Q4AqvK1OHq31f0v0G51x-DRvaS9_o,5234
33
+ bec_widgets/utils/bec_dispatcher.py,sha256=xJrsHJ-flLFGA-XCa6wCsMzMpRu1fAy9IcUuLh6igSo,5569
34
34
  bec_widgets/utils/bec_table.py,sha256=Xy5qM343K8EvEpB4g_129b63yo1wdEvEY3wqxB_p_Iw,716
35
35
  bec_widgets/utils/colors.py,sha256=JsLxzkxbw-I8GIuvnIKyiM83n0edhyMG2Fa4Ffm62ww,2392
36
36
  bec_widgets/utils/container_utils.py,sha256=rL-ryupQ4-7Y8mKNup5aklXleOfXSd97uwXTa9UR90A,1492
37
37
  bec_widgets/utils/crosshair.py,sha256=5gG4G6jjtp6Bd1Y5EySHP2EkmtR4mYdxLCgVtx9fokE,9406
38
38
  bec_widgets/utils/ctrl_c.py,sha256=NMJlPDZcuqMUGykyhuZY5Ibed4yRI1K_uh16z2MmlXQ,1198
39
39
  bec_widgets/utils/entry_validator.py,sha256=88OpJqaldMjiaEk7F9rRcwPuCOTLCzXmAlSEL-_7iXU,1296
40
+ bec_widgets/utils/plugin_utils.py,sha256=wi5x7VVFee0PqAcP-EqPWjYfSe-LLhaK93y6qmnbLIw,1487
40
41
  bec_widgets/utils/rpc_decorator.py,sha256=pIvtqySQLnuS7l2Ti_UAe4WX7CRivZnsE5ZdKAihxh0,479
41
42
  bec_widgets/utils/thread_checker.py,sha256=rDNuA3X6KQyA7JPb67mccTg0z8YkInynLAENQDQpbuE,1607
42
43
  bec_widgets/utils/validator_delegate.py,sha256=Emj1WF6W8Ke1ruBWUfmHdVJpmOSPezuOt4zvQTay_44,442
@@ -78,7 +79,7 @@ tests/unit_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
78
79
  tests/unit_tests/client_mocks.py,sha256=LNUgI9Ccv5Ol7_pmybIhoVqZZem1RPIsTDk7ZTARNls,4128
79
80
  tests/unit_tests/conftest.py,sha256=KrnktXPWmZhnKNue-xGWOLD1XGEvdz9Vf7V2eO3XQ3A,596
80
81
  tests/unit_tests/test_bec_connector.py,sha256=f2XXGGw3NoZLIUrDuZuEWwF_ttOYmmquCgUrV5XkIOY,1951
81
- tests/unit_tests/test_bec_dispatcher.py,sha256=MtNyfC7-Y4na-Fwf1ny9raHBqE45eSnQNWSqqAx79FU,1857
82
+ tests/unit_tests/test_bec_dispatcher.py,sha256=WW-wlzSgZA_h2Qx5EMvXfMlb2i7RYbWj2Cz3S2EzsZQ,4178
82
83
  tests/unit_tests/test_bec_figure.py,sha256=T4k-E1D3sjTTDTFZGdTFDQv0EYNQ_R-QbWOM7pQwFw4,7926
83
84
  tests/unit_tests/test_bec_monitor.py,sha256=mN7gBY7oXY6j65zzihpy8r-FvwVoCQlie3F6SoVq0mo,7042
84
85
  tests/unit_tests/test_bec_motor_map.py,sha256=IXSfitUGxOPqmngwVNPK5nwi2QDcXWjBkGNb0dBZDxQ,4611
@@ -99,8 +100,8 @@ tests/unit_tests/test_widget_io.py,sha256=FeL3ZYSBQnRt6jxj8VGYw1cmcicRQyHKleahw7
99
100
  tests/unit_tests/test_yaml_dialog.py,sha256=HNrqferkdg02-9ieOhhI2mr2Qvt7GrYgXmQ061YCTbg,5794
100
101
  tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
102
  tests/unit_tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
102
- bec_widgets-0.50.1.dist-info/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
103
- bec_widgets-0.50.1.dist-info/METADATA,sha256=PB4Q72Tfk5RP4h940hR5N3NSNJyYZ5TGm_nzUetk5z8,3719
104
- bec_widgets-0.50.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
105
- bec_widgets-0.50.1.dist-info/top_level.txt,sha256=EXCwhJYmXmd1DjYYL3hrGsddX-97IwYSiIHrf27FFVk,18
106
- bec_widgets-0.50.1.dist-info/RECORD,,
103
+ bec_widgets-0.51.0.dist-info/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
104
+ bec_widgets-0.51.0.dist-info/METADATA,sha256=PwzbcGJ0Sy4MSwKmxEb2lh4XZVPxC_fHNjHyHVcPFHg,3724
105
+ bec_widgets-0.51.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
106
+ bec_widgets-0.51.0.dist-info/top_level.txt,sha256=EXCwhJYmXmd1DjYYL3hrGsddX-97IwYSiIHrf27FFVk,18
107
+ bec_widgets-0.51.0.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  # pylint: disable = no-name-in-module,missing-class-docstring, missing-module-docstring
2
+ import threading
2
3
  import time
3
4
  from unittest import mock
4
5
 
@@ -13,8 +14,9 @@ from bec_widgets.utils.bec_dispatcher import QtRedisConnector
13
14
 
14
15
 
15
16
  @pytest.fixture
16
- def bec_dispatcher_w_connector(bec_dispatcher, topics_msg_list):
17
+ def bec_dispatcher_w_connector(bec_dispatcher, topics_msg_list, send_msg_event):
17
18
  def pubsub_msg_generator():
19
+ send_msg_event.wait()
18
20
  for topic, msg in topics_msg_list:
19
21
  yield {"channel": topic.encode(), "pattern": None, "data": msg}
20
22
  while True:
@@ -33,6 +35,11 @@ def bec_dispatcher_w_connector(bec_dispatcher, topics_msg_list):
33
35
  dummy_msg = MsgpackSerialization.dumps(ScanMessage(point_id=0, scan_id="0", data={}))
34
36
 
35
37
 
38
+ @pytest.fixture
39
+ def send_msg_event():
40
+ return threading.Event()
41
+
42
+
36
43
  @pytest.mark.parametrize(
37
44
  "topics_msg_list",
38
45
  [
@@ -43,7 +50,7 @@ dummy_msg = MsgpackSerialization.dumps(ScanMessage(point_id=0, scan_id="0", data
43
50
  )
44
51
  ],
45
52
  )
46
- def test_dispatcher_disconnect_all(bec_dispatcher_w_connector, qtbot):
53
+ def test_dispatcher_disconnect_all(bec_dispatcher_w_connector, qtbot, send_msg_event):
47
54
  bec_dispatcher = bec_dispatcher_w_connector
48
55
  cb1 = mock.Mock(spec=[])
49
56
  cb2 = mock.Mock(spec=[])
@@ -53,7 +60,81 @@ def test_dispatcher_disconnect_all(bec_dispatcher_w_connector, qtbot):
53
60
  bec_dispatcher.connect_slot(cb2, "topic2")
54
61
  bec_dispatcher.connect_slot(cb2, "topic3")
55
62
  assert len(bec_dispatcher.client.connector._topics_cb) == 3
63
+ send_msg_event.set()
64
+ qtbot.wait(10)
65
+ assert cb1.call_count == 2
66
+ assert cb2.call_count == 2
56
67
 
57
68
  bec_dispatcher.disconnect_all()
58
69
 
59
70
  assert len(bec_dispatcher.client.connector._topics_cb) == 0
71
+
72
+
73
+ @pytest.mark.parametrize(
74
+ "topics_msg_list",
75
+ [
76
+ (
77
+ ("topic1", dummy_msg),
78
+ ("topic2", dummy_msg),
79
+ )
80
+ ],
81
+ )
82
+ def test_dispatcher_disconnect_one(bec_dispatcher_w_connector, qtbot, send_msg_event):
83
+ # test for BEC issue #276
84
+ bec_dispatcher = bec_dispatcher_w_connector
85
+ cb1 = mock.Mock(spec=[])
86
+ cb2 = mock.Mock(spec=[])
87
+
88
+ bec_dispatcher.connect_slot(cb1, "topic1")
89
+ bec_dispatcher.connect_slot(cb2, "topic2")
90
+ assert len(bec_dispatcher.client.connector._topics_cb) == 2
91
+ bec_dispatcher.disconnect_slot(cb1, "topic1")
92
+ assert len(bec_dispatcher.client.connector._topics_cb) == 1
93
+
94
+ send_msg_event.set()
95
+ qtbot.wait(10)
96
+ assert cb1.call_count == 0
97
+ cb2.assert_called_once()
98
+
99
+
100
+ @pytest.mark.parametrize("topics_msg_list", [(("topic1", dummy_msg),)])
101
+ def test_dispatcher_2_cb_same_topic(bec_dispatcher_w_connector, qtbot, send_msg_event):
102
+ # test for BEC issue #276
103
+ bec_dispatcher = bec_dispatcher_w_connector
104
+ cb1 = mock.Mock(spec=[])
105
+ cb2 = mock.Mock(spec=[])
106
+
107
+ bec_dispatcher.connect_slot(cb1, "topic1")
108
+ bec_dispatcher.connect_slot(cb2, "topic1")
109
+ assert len(bec_dispatcher.client.connector._topics_cb) == 1
110
+ bec_dispatcher.disconnect_slot(cb1, "topic1")
111
+
112
+ send_msg_event.set()
113
+ qtbot.wait(10)
114
+ assert cb1.call_count == 0
115
+ cb2.assert_called_once()
116
+
117
+
118
+ @pytest.mark.parametrize(
119
+ "topics_msg_list",
120
+ [
121
+ (
122
+ ("topic1", dummy_msg),
123
+ ("topic2", dummy_msg),
124
+ )
125
+ ],
126
+ )
127
+ def test_dispatcher_2_topic_same_cb(bec_dispatcher_w_connector, qtbot, send_msg_event):
128
+ # test for BEC issue #276
129
+ bec_dispatcher = bec_dispatcher_w_connector
130
+ cb1 = mock.Mock(spec=[])
131
+
132
+ bec_dispatcher.connect_slot(cb1, "topic1")
133
+ bec_dispatcher.connect_slot(cb1, "topic2")
134
+ assert len(bec_dispatcher.client.connector._topics_cb) == 2
135
+ bec_dispatcher.disconnect_slot(cb1, "topic1")
136
+ assert len(bec_dispatcher.client.connector._topics_cb) == 1
137
+
138
+ send_msg_event.set()
139
+ qtbot.wait(10)
140
+ cb1.assert_called_once()