bec-widgets 0.50.0__py3-none-any.whl → 0.50.2__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.
@@ -12,7 +12,7 @@ import uuid
12
12
  from functools import wraps
13
13
  from typing import TYPE_CHECKING
14
14
 
15
- from bec_lib import MessageEndpoints, messages
15
+ from bec_lib import MessageEndpoints, ServiceConfig, messages
16
16
  from bec_lib.connector import MessageObject
17
17
  from bec_lib.device import DeviceBase
18
18
  from qtpy.QtCore import QCoreApplication
@@ -136,10 +136,11 @@ class BECFigureClientMixin:
136
136
  """
137
137
  self._start_update_script()
138
138
  # pylint: disable=subprocess-run-check
139
+ config = self._client._service_config.redis
139
140
  monitor_module = importlib.import_module("bec_widgets.cli.server")
140
141
  monitor_path = monitor_module.__file__
141
142
 
142
- command = [sys.executable, "-u", monitor_path, "--id", self._gui_id]
143
+ command = [sys.executable, "-u", monitor_path, "--id", self._gui_id, "--config", config]
143
144
  self._process = subprocess.Popen(
144
145
  command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
145
146
  )
bec_widgets/cli/server.py CHANGED
@@ -15,8 +15,10 @@ from bec_widgets.widgets.plots import BECCurve, BECImageShow, BECWaveform
15
15
  class BECWidgetsCLIServer:
16
16
  WIDGETS = [BECWaveform, BECFigure, BECCurve, BECImageShow]
17
17
 
18
- def __init__(self, gui_id: str = None, dispatcher: BECDispatcher = None, client=None) -> None:
19
- self.dispatcher = BECDispatcher() if dispatcher is None else dispatcher
18
+ def __init__(
19
+ self, gui_id: str = None, dispatcher: BECDispatcher = None, client=None, config=None
20
+ ) -> None:
21
+ self.dispatcher = BECDispatcher(config=config) if dispatcher is None else dispatcher
20
22
  self.client = self.dispatcher.client if client is None else client
21
23
  self.client.start()
22
24
  self.gui_id = gui_id
@@ -32,7 +34,7 @@ class BECWidgetsCLIServer:
32
34
  self._shutdown_event = False
33
35
  self._heartbeat_timer = QTimer()
34
36
  self._heartbeat_timer.timeout.connect(self.emit_heartbeat)
35
- self._heartbeat_timer.start(1000) # Emit heartbeat every 1 seconds
37
+ self._heartbeat_timer.start(200) # Emit heartbeat every 1 seconds
36
38
 
37
39
  def on_rpc_update(self, msg: dict, metadata: dict):
38
40
  request_id = metadata.get("request_id")
@@ -125,11 +127,12 @@ if __name__ == "__main__": # pragma: no cover
125
127
 
126
128
  parser = argparse.ArgumentParser(description="BEC Widgets CLI Server")
127
129
  parser.add_argument("--id", type=str, help="The id of the server")
130
+ parser.add_argument("--config", type=str, help="Config to connect to redis.")
128
131
 
129
132
  args = parser.parse_args()
130
133
 
131
- server = BECWidgetsCLIServer(gui_id=args.id)
132
- # server = BECWidgetsCLIServer(gui_id="test")
134
+ server = BECWidgetsCLIServer(gui_id=args.id, config=args.config)
135
+ # server = BECWidgetsCLIServer(gui_id="test",config="awi-bec-dev-01:6379")
133
136
 
134
137
  fig = server.fig
135
138
  win.setCentralWidget(fig)
@@ -6,7 +6,7 @@ from collections.abc import Callable
6
6
  from typing import TYPE_CHECKING, Union
7
7
 
8
8
  import redis
9
- from bec_lib import BECClient
9
+ from bec_lib import BECClient, ServiceConfig
10
10
  from bec_lib.redis_connector import MessageObject, RedisConnector
11
11
  from qtpy.QtCore import QObject
12
12
  from qtpy.QtCore import Signal as pyqtSignal
@@ -71,13 +71,13 @@ class BECDispatcher:
71
71
  _instance = None
72
72
  _initialized = False
73
73
 
74
- def __new__(cls, client=None, *args, **kwargs):
74
+ def __new__(cls, client=None, config: str = None, *args, **kwargs):
75
75
  if cls._instance is None:
76
76
  cls._instance = super(BECDispatcher, cls).__new__(cls)
77
77
  cls._initialized = False
78
78
  return cls._instance
79
79
 
80
- def __init__(self, client=None):
80
+ def __init__(self, client=None, config: str = None):
81
81
  if self._initialized:
82
82
  return
83
83
 
@@ -85,7 +85,14 @@ class BECDispatcher:
85
85
  self.client = client
86
86
 
87
87
  if self.client is None:
88
- self.client = BECClient(connector_cls=QtRedisConnector, forced=True)
88
+ if config is not None:
89
+ host, port = config.split(":")
90
+ redis_config = {"host": host, "port": port}
91
+ self.client = BECClient(
92
+ config=ServiceConfig(redis=redis_config), connector_cls=QtRedisConnector
93
+ ) # , forced=True)
94
+ else:
95
+ self.client = BECClient(connector_cls=QtRedisConnector) # , forced=True)
89
96
  else:
90
97
  if self.client.started:
91
98
  # have to reinitialize client to use proper connector
@@ -122,7 +129,15 @@ class BECDispatcher:
122
129
  self._slots[slot].update(set(topics_str))
123
130
 
124
131
  def disconnect_slot(self, slot: Callable, topics: Union[str, list]):
125
- 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)
126
141
  topics_str, _ = self.client.connector._convert_endpointinfo(topics)
127
142
  self._slots[slot].difference_update(set(topics_str))
128
143
  if not self._slots[slot]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bec-widgets
3
- Version: 0.50.0
3
+ Version: 0.50.2
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
@@ -3,10 +3,10 @@ bec_widgets/cli/__init__.py,sha256=ULoNTVnv2UeSSjhFq3uCQJ-0JTJf9oU76l27aRiizL4,7
3
3
  bec_widgets/cli/auto_updates.py,sha256=ptZeBKr13o9THc8oKLn93K_16i6G3pxzw8hZ4MUgjW4,3845
4
4
  bec_widgets/cli/bec_widgets_icon.png,sha256=pRCGpoOtwyZl97fBV_CHcGppliErzd0qQkCXLxjbp-s,5760
5
5
  bec_widgets/cli/client.py,sha256=AvkaEDKB8cZFm2WS5JWIusMXtcqErEeP2Ayk9l7iAp8,39163
6
- bec_widgets/cli/client_utils.py,sha256=FBuU0LTi1lQrzRIH_4ued_isC_iknYMAaNxnSTsJvTw,10118
6
+ bec_widgets/cli/client_utils.py,sha256=IzyVAcko47vC47_oT0fAi9M-8OrZ2qmUfMlOAvQGuhU,10205
7
7
  bec_widgets/cli/generate_cli.py,sha256=JLqUlUgfz_f_4KHPRUAN-Xli-K7uNOc8-F-LkAC7Scw,4004
8
8
  bec_widgets/cli/rpc_register.py,sha256=OZOWX0IKGXqDsnrUYi0Irl_zpPS4Q_JPCV9JQfN6YYw,2212
9
- bec_widgets/cli/server.py,sha256=Xru-gLqDhTRIu6EnYIsFXeinMDW_NbtKYtPivlfdBY8,4772
9
+ bec_widgets/cli/server.py,sha256=UpbegcLn2nd9gPF2swNfPQGHVwG14ZjQBpZKCAjQT4E,4942
10
10
  bec_widgets/examples/__init__.py,sha256=WWQ0cu7m8sA4Ehy-DWdTIqSISjaHsbxhsNmNrMnhDZU,202
11
11
  bec_widgets/examples/eiger_plot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  bec_widgets/examples/eiger_plot/eiger_plot.py,sha256=Uxl2Usf8jEzaX7AT8zVqa1x8ZIEgI1HmazSlb-tRFWE,10359
@@ -30,7 +30,7 @@ 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=sLv9CmJ3GKGDhvCXCDmuKtNRlI4w1oWxuQu_Mq2mDDY,4840
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
@@ -78,7 +78,7 @@ tests/unit_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
78
78
  tests/unit_tests/client_mocks.py,sha256=LNUgI9Ccv5Ol7_pmybIhoVqZZem1RPIsTDk7ZTARNls,4128
79
79
  tests/unit_tests/conftest.py,sha256=KrnktXPWmZhnKNue-xGWOLD1XGEvdz9Vf7V2eO3XQ3A,596
80
80
  tests/unit_tests/test_bec_connector.py,sha256=f2XXGGw3NoZLIUrDuZuEWwF_ttOYmmquCgUrV5XkIOY,1951
81
- tests/unit_tests/test_bec_dispatcher.py,sha256=MtNyfC7-Y4na-Fwf1ny9raHBqE45eSnQNWSqqAx79FU,1857
81
+ tests/unit_tests/test_bec_dispatcher.py,sha256=WW-wlzSgZA_h2Qx5EMvXfMlb2i7RYbWj2Cz3S2EzsZQ,4178
82
82
  tests/unit_tests/test_bec_figure.py,sha256=T4k-E1D3sjTTDTFZGdTFDQv0EYNQ_R-QbWOM7pQwFw4,7926
83
83
  tests/unit_tests/test_bec_monitor.py,sha256=mN7gBY7oXY6j65zzihpy8r-FvwVoCQlie3F6SoVq0mo,7042
84
84
  tests/unit_tests/test_bec_motor_map.py,sha256=IXSfitUGxOPqmngwVNPK5nwi2QDcXWjBkGNb0dBZDxQ,4611
@@ -99,8 +99,8 @@ tests/unit_tests/test_widget_io.py,sha256=FeL3ZYSBQnRt6jxj8VGYw1cmcicRQyHKleahw7
99
99
  tests/unit_tests/test_yaml_dialog.py,sha256=HNrqferkdg02-9ieOhhI2mr2Qvt7GrYgXmQ061YCTbg,5794
100
100
  tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
101
  tests/unit_tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
102
- bec_widgets-0.50.0.dist-info/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
103
- bec_widgets-0.50.0.dist-info/METADATA,sha256=2i8dBEDDsAAbm4Hgxf_Vt5h2weKOK-dQ0KKtb75wlG0,3719
104
- bec_widgets-0.50.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
105
- bec_widgets-0.50.0.dist-info/top_level.txt,sha256=EXCwhJYmXmd1DjYYL3hrGsddX-97IwYSiIHrf27FFVk,18
106
- bec_widgets-0.50.0.dist-info/RECORD,,
102
+ bec_widgets-0.50.2.dist-info/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
103
+ bec_widgets-0.50.2.dist-info/METADATA,sha256=1y0vUcfn4EHSqdP0RgJQ99mJRfVR1Qq7u16T95__1S4,3719
104
+ bec_widgets-0.50.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
105
+ bec_widgets-0.50.2.dist-info/top_level.txt,sha256=EXCwhJYmXmd1DjYYL3hrGsddX-97IwYSiIHrf27FFVk,18
106
+ bec_widgets-0.50.2.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()