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.
- bec_widgets/utils/bec_dispatcher.py +9 -1
- bec_widgets/utils/plugin_utils.py +40 -0
- {bec_widgets-0.50.1.dist-info → bec_widgets-0.51.0.dist-info}/METADATA +8 -8
- {bec_widgets-0.50.1.dist-info → bec_widgets-0.51.0.dist-info}/RECORD +8 -7
- tests/unit_tests/test_bec_dispatcher.py +83 -2
- {bec_widgets-0.50.1.dist-info → bec_widgets-0.51.0.dist-info}/LICENSE +0 -0
- {bec_widgets-0.50.1.dist-info → bec_widgets-0.51.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.50.1.dist-info → bec_widgets-0.51.0.dist-info}/top_level.txt +0 -0
@@ -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
|
-
|
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.
|
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
|
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
|
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
|
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
|
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
|
62
|
+
pip install bec_widgets[pyqt6]
|
63
63
|
```
|
64
64
|
or
|
65
65
|
|
66
66
|
```bash
|
67
|
-
pip install
|
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=
|
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=
|
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.
|
103
|
-
bec_widgets-0.
|
104
|
-
bec_widgets-0.
|
105
|
-
bec_widgets-0.
|
106
|
-
bec_widgets-0.
|
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()
|
File without changes
|
File without changes
|
File without changes
|