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.
- CHANGELOG.md +42 -40
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +82 -3
- bec_widgets/cli/client_utils.py +46 -34
- bec_widgets/cli/server.py +61 -18
- bec_widgets/utils/bec_dispatcher.py +14 -6
- bec_widgets/widgets/bec_status_box/__init__.py +0 -0
- bec_widgets/widgets/bec_status_box/bec_status_box.py +352 -0
- bec_widgets/widgets/bec_status_box/status_item.py +171 -0
- {bec_widgets-0.66.1.dist-info → bec_widgets-0.68.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.66.1.dist-info → bec_widgets-0.68.0.dist-info}/RECORD +24 -18
- docs/developer/widgets/widgets.md +0 -1
- docs/requirements.txt +1 -0
- docs/user/getting_started/installation.md +2 -2
- docs/user/widgets/bec_status_box.gif +0 -0
- docs/user/widgets/bec_status_box.md +30 -0
- docs/user/widgets/buttons.md +0 -1
- docs/user/widgets/widgets.md +2 -0
- pyproject.toml +1 -1
- tests/end-2-end/conftest.py +1 -4
- tests/unit_tests/test_bec_status_box.py +152 -0
- {bec_widgets-0.66.1.dist-info → bec_widgets-0.68.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.66.1.dist-info → bec_widgets-0.68.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.66.1.dist-info → bec_widgets-0.68.0.dist-info}/licenses/LICENSE +0 -0
@@ -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
|
+

|
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
|
+
|
docs/user/widgets/buttons.md
CHANGED
docs/user/widgets/widgets.md
CHANGED
pyproject.toml
CHANGED
tests/end-2-end/conftest.py
CHANGED
@@ -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,
|
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
|
File without changes
|
File without changes
|
File without changes
|