bec-widgets 0.66.1__py3-none-any.whl → 0.68.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.
@@ -13,13 +13,13 @@ To install BEC Widgets using the pip package manager, execute the following comm
13
13
 
14
14
 
15
15
  ```bash
16
- pip install bec_widgets[pyqt6]
16
+ pip install 'bec_widgets[pyqt6]'
17
17
  ```
18
18
 
19
19
  In case you want to use PyQt5, you can install it by using the following command:
20
20
 
21
21
  ```bash
22
- pip install bec_widgets[pyqt5]
22
+ pip install 'bec_widgets[pyqt5]'
23
23
  ```
24
24
 
25
25
  **Troubleshooting**
Binary file
@@ -0,0 +1,30 @@
1
+ (user.widgets.bec_status_box)=
2
+ # BEC Status Box
3
+ **Purpose:**
4
+
5
+ The [BECStatusBox]](/api_reference/_autosummary/bec_widgets.cli.client.BECStatusBox) Widget is a widget that allows you to monitor the status/health of the all running BEC processes. The widget generates the view automatically and updates the status of the processes in real-time. The top level indicates the overall state of the BEC core services (DeviceServer, ScanServer, SciHub, ScanBundler and FileWriter), but you can also see the status of each individual process by opening the collapsed view. In the collapsed view, you can double click on each process to get a popup window with live updates of the metrics for each process in real-time.
6
+
7
+ **Key Features:**
8
+
9
+ - monitor the state of individual BEC services.
10
+ - automatically track BEC services, i.e. additional clients connecting.
11
+ - live-updates of the metrics for each process.
12
+
13
+ **Example of Use:**
14
+ ![BECStatus](./bec_status_box.gif)
15
+
16
+ **Code example:**
17
+
18
+ The following code snipped demonstrates how to create a `BECStatusBox` widget using BEC Widgets within BEC.
19
+ ```python
20
+ bec_status_box = gui.add_dock().add_widget("BECStatusBox")
21
+ ```
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
@@ -1,5 +1,4 @@
1
1
  (user.widgets.buttons)=
2
-
3
2
  # Buttons Widgets
4
3
 
5
4
  This section consolidates various custom buttons used within the BEC GUIs, facilitating the integration of these
@@ -13,6 +13,8 @@ spiral_progress_bar/
13
13
  website/
14
14
  buttons/
15
15
  text_box/
16
+ bec_status_box/
17
+
16
18
  ```
17
19
 
18
20
 
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "0.66.1"
7
+ version = "0.68.0"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [
@@ -29,9 +29,7 @@ def gui_id():
29
29
  @contextmanager
30
30
  def plot_server(gui_id, klass, client_lib):
31
31
  dispatcher = BECDispatcher(client=client_lib) # Has to init singleton with fixture client
32
- process, output_thread = _start_plot_process(
33
- gui_id, klass, client_lib._client._service_config.redis
34
- )
32
+ process, _ = _start_plot_process(gui_id, klass, client_lib._client._service_config.config_path)
35
33
  try:
36
34
  while client_lib._client.connector.get(MessageEndpoints.gui_heartbeat(gui_id)) is None:
37
35
  time.sleep(0.3)
@@ -39,7 +37,6 @@ def plot_server(gui_id, klass, client_lib):
39
37
  finally:
40
38
  process.terminate()
41
39
  process.wait()
42
- output_thread.join()
43
40
  dispatcher.disconnect_all()
44
41
  dispatcher.reset_singleton()
45
42
 
@@ -0,0 +1,152 @@
1
+ import re
2
+ from unittest import mock
3
+
4
+ import pytest
5
+ from bec_lib.messages import BECStatus, ServiceMetricMessage, StatusMessage
6
+ from qtpy.QtCore import QMetaMethod
7
+
8
+ from bec_widgets.widgets.bec_status_box.bec_status_box import BECServiceInfoContainer, BECStatusBox
9
+
10
+ from .client_mocks import mocked_client
11
+
12
+
13
+ @pytest.fixture
14
+ def status_box(qtbot, mocked_client):
15
+ with mock.patch(
16
+ "bec_widgets.widgets.bec_status_box.bec_status_box.BECServiceStatusMixin"
17
+ ) as mock_service_status_mixin:
18
+ widget = BECStatusBox(client=mocked_client)
19
+ qtbot.addWidget(widget)
20
+ qtbot.waitExposed(widget)
21
+ yield widget
22
+
23
+
24
+ def test_status_box_init(qtbot, mocked_client):
25
+ with mock.patch(
26
+ "bec_widgets.widgets.bec_status_box.bec_status_box.BECServiceStatusMixin"
27
+ ) as mock_service_status_mixin:
28
+ name = "my test"
29
+ widget = BECStatusBox(parent=None, service_name=name, client=mocked_client)
30
+ qtbot.addWidget(widget)
31
+ qtbot.waitExposed(widget)
32
+ assert widget.headerItem().DontShowIndicator.value == 1
33
+ assert widget.children()[0].children()[0].config.service_name == name
34
+
35
+
36
+ def test_update_top_item(qtbot, mocked_client):
37
+ with (
38
+ mock.patch(
39
+ "bec_widgets.widgets.bec_status_box.bec_status_box.BECServiceStatusMixin"
40
+ ) as mock_service_status_mixin,
41
+ mock.patch(
42
+ "bec_widgets.widgets.bec_status_box.status_item.StatusItem.update_config"
43
+ ) as mock_update,
44
+ ):
45
+ name = "my test"
46
+ widget = BECStatusBox(parent=None, service_name=name, client=mocked_client)
47
+ qtbot.addWidget(widget)
48
+ qtbot.waitExposed(widget)
49
+ widget.update_top_item_status(status="RUNNING")
50
+ assert widget.bec_service_info_container[name].status == "RUNNING"
51
+ assert mock_update.call_args == mock.call(widget.bec_service_info_container[name].dict())
52
+
53
+
54
+ def test_create_status_widget(status_box):
55
+ name = "test_service"
56
+ status = BECStatus.IDLE
57
+ info = {"test": "test"}
58
+ metrics = {"metric": "test_metric"}
59
+ item = status_box._create_status_widget(name, status, info, metrics)
60
+ assert item.config.service_name == name
61
+ assert item.config.status == status.name
62
+ assert item.config.info == info
63
+ assert item.config.metrics == metrics
64
+
65
+
66
+ def test_bec_service_container(status_box):
67
+ name = "test_service"
68
+ status = BECStatus.IDLE
69
+ info = {"test": "test"}
70
+ metrics = {"metric": "test_metric"}
71
+ expected_return = BECServiceInfoContainer(
72
+ service_name=name, status=status, info=info, metrics=metrics
73
+ )
74
+ assert status_box.service_name in status_box.bec_service_info_container
75
+ assert len(status_box.bec_service_info_container) == 1
76
+ status_box._update_bec_service_container(name, status, info, metrics)
77
+ assert len(status_box.bec_service_info_container) == 2
78
+ assert status_box.bec_service_info_container[name] == expected_return
79
+
80
+
81
+ def test_add_tree_item(status_box):
82
+ name = "test_service"
83
+ status = BECStatus.IDLE
84
+ info = {"test": "test"}
85
+ metrics = {"metric": "test_metric"}
86
+ assert len(status_box.children()[0].children()) == 1
87
+ status_box.add_tree_item(name, status, info, metrics)
88
+ assert len(status_box.children()[0].children()) == 2
89
+ assert name in status_box.tree_items
90
+
91
+
92
+ def test_update_service_status(status_box):
93
+ """Also checks check redundant tree items"""
94
+ name = "test_service"
95
+ status = BECStatus.IDLE
96
+ info = {"test": "test"}
97
+ metrics = {"metric": "test_metric"}
98
+ status_box.add_tree_item(name, status, info, {})
99
+ not_connected_name = "invalid_service"
100
+ status_box.add_tree_item(not_connected_name, status, info, metrics)
101
+
102
+ services_status = {name: StatusMessage(name=name, status=status, info=info)}
103
+ services_metrics = {name: ServiceMetricMessage(name=name, metrics=metrics)}
104
+
105
+ with mock.patch.object(status_box, "update_core_services", return_value=services_status):
106
+ assert not_connected_name in status_box.tree_items
107
+ status_box.update_service_status(services_status, services_metrics)
108
+ assert status_box.tree_items[name][1].config.metrics == metrics
109
+ assert not_connected_name not in status_box.tree_items
110
+
111
+
112
+ def test_update_core_services(qtbot, mocked_client):
113
+ with (
114
+ mock.patch(
115
+ "bec_widgets.widgets.bec_status_box.bec_status_box.BECServiceStatusMixin"
116
+ ) as mock_service_status_mixin,
117
+ mock.patch(
118
+ "bec_widgets.widgets.bec_status_box.bec_status_box.BECStatusBox.update_top_item_status"
119
+ ) as mock_update,
120
+ ):
121
+ name = "my test"
122
+ status_box = BECStatusBox(parent=None, service_name=name, client=mocked_client)
123
+ qtbot.addWidget(status_box)
124
+ qtbot.waitExposed(status_box)
125
+ status_box.CORE_SERVICES = ["test_service"]
126
+ name = "test_service"
127
+ status = BECStatus.RUNNING
128
+ info = {"test": "test"}
129
+ metrics = {"metric": "test_metric"}
130
+ services_status = {name: StatusMessage(name=name, status=status, info=info)}
131
+ services_metrics = {name: ServiceMetricMessage(name=name, metrics=metrics)}
132
+
133
+ status_box.update_core_services(services_status, services_metrics)
134
+ assert mock_update.call_args == mock.call(status.name)
135
+
136
+ status = BECStatus.IDLE
137
+ services_status = {name: StatusMessage(name=name, status=status, info=info)}
138
+ services_metrics = {name: ServiceMetricMessage(name=name, metrics=metrics)}
139
+ status_box.update_core_services(services_status, services_metrics)
140
+ assert mock_update.call_args == mock.call("ERROR")
141
+
142
+
143
+ def test_double_click_item(status_box):
144
+ name = "test_service"
145
+ status = BECStatus.IDLE
146
+ info = {"test": "test"}
147
+ metrics = {"MyData": "This should be shown nicely"}
148
+ status_box.add_tree_item(name, status, info, metrics)
149
+ item, status_item = status_box.tree_items[name]
150
+ with mock.patch.object(status_item, "show_popup") as mock_show_popup:
151
+ status_box.itemDoubleClicked.emit(item, 0)
152
+ assert mock_show_popup.call_count == 1