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.
@@ -0,0 +1,352 @@
1
+ """This module contains the BECStatusBox widget, which displays the status of different BEC services in a collapsible tree widget.
2
+ The widget automatically updates the status of all running BEC services, and displays their status.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import sys
8
+ from typing import TYPE_CHECKING
9
+
10
+ import qdarktheme
11
+ from bec_lib.utils.import_utils import lazy_import_from
12
+ from pydantic import BaseModel, Field, field_validator
13
+ from qtpy.QtCore import QObject, QTimer, Signal, Slot
14
+ from qtpy.QtWidgets import QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
15
+
16
+ from bec_widgets.utils.bec_connector import BECConnector, ConnectionConfig
17
+ from bec_widgets.widgets.bec_status_box.status_item import StatusItem
18
+
19
+ if TYPE_CHECKING:
20
+ from bec_lib.client import BECClient
21
+
22
+ # TODO : Put normal imports back when Pydantic gets faster
23
+ BECStatus = lazy_import_from("bec_lib.messages", ("BECStatus",))
24
+
25
+
26
+ class BECStatusBoxConfig(ConnectionConfig):
27
+ pass
28
+
29
+
30
+ class BECServiceInfoContainer(BaseModel):
31
+ """Container to store information about the BEC services."""
32
+
33
+ service_name: str
34
+ status: BECStatus | str = Field(
35
+ default="NOTCONNECTED",
36
+ description="The status of the service. Can be any of the BECStatus names, or NOTCONNECTED.",
37
+ )
38
+ info: dict
39
+ metrics: dict | None
40
+ model_config: dict = {"validate_assignment": True}
41
+
42
+ @field_validator("status")
43
+ @classmethod
44
+ def validate_status(cls, v):
45
+ """Validate input for status. Accept BECStatus and NOTCONNECTED.
46
+
47
+ Args:
48
+ v (BECStatus | str): The input value.
49
+
50
+ Returns:
51
+ str: The validated status.
52
+ """
53
+ if v in list(BECStatus.__members__.values()):
54
+ return v.name
55
+ if v in list(BECStatus.__members__.keys()) or v == "NOTCONNECTED":
56
+ return v
57
+ raise ValueError(
58
+ f"Status must be one of {BECStatus.__members__.values()} or 'NOTCONNECTED'. Input {v}"
59
+ )
60
+
61
+
62
+ class BECServiceStatusMixin(QObject):
63
+ """A mixin class to update the service status, and metrics.
64
+ It emits a signal 'services_update' when the service status is updated.
65
+
66
+ Args:
67
+ client (BECClient): The client object to connect to the BEC server.
68
+ """
69
+
70
+ services_update = Signal(dict, dict)
71
+
72
+ def __init__(self, client: BECClient):
73
+ super().__init__()
74
+ self.client = client
75
+ self._service_update_timer = QTimer()
76
+ self._service_update_timer.timeout.connect(self._get_service_status)
77
+ self._service_update_timer.start(1000)
78
+
79
+ def _get_service_status(self):
80
+ """Pull latest service and metrics updates from REDIS for all services, and emit both via 'services_update' signal."""
81
+ # pylint: disable=protected-access
82
+ self.client._update_existing_services()
83
+ self.services_update.emit(self.client._services_info, self.client._services_metric)
84
+
85
+
86
+ class BECStatusBox(BECConnector, QTreeWidget):
87
+ """A widget to display the status of different BEC services.
88
+ This widget automatically updates the status of all running BEC services, and displays their status.
89
+ Information about the individual services is collapsible, and double clicking on
90
+ the individual service will display the metrics about the service.
91
+
92
+ Args:
93
+ parent Optional : The parent widget for the BECStatusBox. Defaults to None.
94
+ service_name Optional(str): The name of the top service label. Defaults to "BEC Server".
95
+ client Optional(BECClient): The client object to connect to the BEC server. Defaults to None
96
+ config Optional(BECStatusBoxConfig | dict): The configuration for the status box. Defaults to None.
97
+ gui_id Optional(str): The unique id for the widget. Defaults to None.
98
+ """
99
+
100
+ CORE_SERVICES = ["DeviceServer", "ScanServer", "SciHub", "ScanBundler", "FileWriterManager"]
101
+
102
+ service_update = Signal(dict)
103
+ bec_core_state = Signal(str)
104
+
105
+ def __init__(
106
+ self,
107
+ parent=None,
108
+ service_name: str = "BEC Server",
109
+ client: BECClient = None,
110
+ config: BECStatusBoxConfig | dict = None,
111
+ gui_id: str = None,
112
+ ):
113
+ if config is None:
114
+ config = BECStatusBoxConfig(widget_class=self.__class__.__name__)
115
+ else:
116
+ if isinstance(config, dict):
117
+ config = BECStatusBoxConfig(**config)
118
+ super().__init__(client=client, config=config, gui_id=gui_id)
119
+ QTreeWidget.__init__(self, parent=parent)
120
+
121
+ self.service_name = service_name
122
+ self.config = config
123
+
124
+ self.bec_service_info_container = {}
125
+ self.tree_items = {}
126
+ self.tree_top_item = None
127
+ self.bec_service_status = BECServiceStatusMixin(client=self.client)
128
+
129
+ self.init_ui()
130
+ self.bec_service_status.services_update.connect(self.update_service_status)
131
+ self.bec_core_state.connect(self.update_top_item_status)
132
+ self.itemDoubleClicked.connect(self.on_tree_item_double_clicked)
133
+
134
+ def init_ui(self) -> None:
135
+ """Initialize the UI for the status box, and add QTreeWidget as the basis for the status box."""
136
+ self.init_ui_tree_widget()
137
+ top_label = self._create_status_widget(self.service_name, status=BECStatus.IDLE)
138
+ self.tree_top_item = QTreeWidgetItem()
139
+ self.tree_top_item.setExpanded(True)
140
+ self.tree_top_item.setDisabled(True)
141
+ self.addTopLevelItem(self.tree_top_item)
142
+ self.setItemWidget(self.tree_top_item, 0, top_label)
143
+ self.service_update.connect(top_label.update_config)
144
+
145
+ def _create_status_widget(
146
+ self, service_name: str, status=BECStatus, info: dict = None, metrics: dict = None
147
+ ) -> StatusItem:
148
+ """Creates a StatusItem (QWidget) for the given service, and stores all relevant
149
+ information about the service in the bec_service_info_container.
150
+
151
+ Args:
152
+ service_name (str): The name of the service.
153
+ status (BECStatus): The status of the service.
154
+ info Optional(dict): The information about the service. Default is {}
155
+ metric Optional(dict): Metrics for the respective service. Default is None
156
+
157
+ Returns:
158
+ StatusItem: The status item widget.
159
+ """
160
+ if info is None:
161
+ info = {}
162
+ self._update_bec_service_container(service_name, status, info, metrics)
163
+ item = StatusItem(
164
+ parent=self,
165
+ config={
166
+ "service_name": service_name,
167
+ "status": status.name,
168
+ "info": info,
169
+ "metrics": metrics,
170
+ },
171
+ )
172
+ return item
173
+
174
+ @Slot(str)
175
+ def update_top_item_status(self, status: BECStatus) -> None:
176
+ """Method to update the status of the top item in the tree widget.
177
+ Gets the status from the Signal 'bec_core_state' and updates the StatusItem via the signal 'service_update'.
178
+
179
+ Args:
180
+ status (BECStatus): The state of the core services.
181
+ """
182
+ self.bec_service_info_container[self.service_name].status = status
183
+ self.service_update.emit(self.bec_service_info_container[self.service_name].model_dump())
184
+
185
+ def _update_bec_service_container(
186
+ self, service_name: str, status: BECStatus, info: dict, metrics: dict = None
187
+ ) -> None:
188
+ """Update the bec_service_info_container with the newest status and metrics for the BEC service.
189
+ If information about the service already exists, it will create a new entry.
190
+
191
+ Args:
192
+ service_name (str): The name of the service.
193
+ service_info (StatusMessage): A class containing the service status.
194
+ service_metric (ServiceMetricMessage): A class containing the service metrics.
195
+ """
196
+ container = self.bec_service_info_container.get(service_name, None)
197
+ if container:
198
+ container.status = status
199
+ container.info = info
200
+ container.metrics = metrics
201
+ return
202
+ service_info_item = BECServiceInfoContainer(
203
+ service_name=service_name, status=status, info=info, metrics=metrics
204
+ )
205
+ self.bec_service_info_container.update({service_name: service_info_item})
206
+
207
+ @Slot(dict, dict)
208
+ def update_service_status(self, services_info: dict, services_metric: dict) -> None:
209
+ """Callback function services_metric from BECServiceStatusMixin.
210
+ It updates the status of all services.
211
+
212
+ Args:
213
+ services_info (dict): A dictionary containing the service status for all running BEC services.
214
+ services_metric (dict): A dictionary containing the service metrics for all running BEC services.
215
+ """
216
+ checked = []
217
+ services_info = self.update_core_services(services_info, services_metric)
218
+ checked.extend(self.CORE_SERVICES)
219
+
220
+ for service_name, msg in sorted(services_info.items()):
221
+ checked.append(service_name)
222
+ metric_msg = services_metric.get(service_name, None)
223
+ metrics = metric_msg.metrics if metric_msg else None
224
+ if service_name in self.tree_items:
225
+ self._update_bec_service_container(
226
+ service_name=service_name, status=msg.status, info=msg.info, metrics=metrics
227
+ )
228
+ self.service_update.emit(self.bec_service_info_container[service_name].model_dump())
229
+ continue
230
+
231
+ item_widget = self._create_status_widget(
232
+ service_name=service_name, status=msg.status, info=msg.info, metrics=metrics
233
+ )
234
+ item = QTreeWidgetItem()
235
+ item.setDisabled(True)
236
+ self.service_update.connect(item_widget.update_config)
237
+ self.tree_top_item.addChild(item)
238
+ self.setItemWidget(item, 0, item_widget)
239
+ self.tree_items.update({service_name: (item, item_widget)})
240
+
241
+ self.check_redundant_tree_items(checked)
242
+
243
+ def update_core_services(self, services_info: dict, services_metric: dict) -> dict:
244
+ """Method to process status and metrics updates of core services (stored in CORE_SERVICES).
245
+ If a core services is not connected, it should not be removed from the status widget
246
+
247
+ Args:
248
+ services_info (dict): A dictionary containing the service status of different services.
249
+ services_metric (dict): A dictionary containing the service metrics of different services.
250
+
251
+ Returns:
252
+ dict: The services_info dictionary after removing the info updates related to the CORE_SERVICES
253
+ """
254
+ bec_core_state = "RUNNING"
255
+ for service_name in sorted(self.CORE_SERVICES):
256
+ metric_msg = services_metric.get(service_name, None)
257
+ metrics = metric_msg.metrics if metric_msg else None
258
+ if service_name not in services_info:
259
+ self.bec_service_info_container[service_name].status = "NOTCONNECTED"
260
+ bec_core_state = "ERROR"
261
+ else:
262
+ msg = services_info.pop(service_name)
263
+ self._update_bec_service_container(
264
+ service_name=service_name, status=msg.status, info=msg.info, metrics=metrics
265
+ )
266
+ bec_core_state = (
267
+ "RUNNING" if (msg.status.value > 1 and bec_core_state == "RUNNING") else "ERROR"
268
+ )
269
+
270
+ if service_name in self.tree_items:
271
+ self.service_update.emit(self.bec_service_info_container[service_name].model_dump())
272
+ continue
273
+ self.add_tree_item(service_name, msg.status, msg.info, metrics)
274
+
275
+ self.bec_core_state.emit(bec_core_state)
276
+ return services_info
277
+
278
+ def check_redundant_tree_items(self, checked: list) -> None:
279
+ """Utility method to check and remove redundant objects from the BECStatusBox.
280
+
281
+ Args:
282
+ checked (list): A list of services that are currently running.
283
+ """
284
+ to_be_deleted = [key for key in self.tree_items if key not in checked]
285
+
286
+ for key in to_be_deleted:
287
+ item, _ = self.tree_items.pop(key)
288
+ self.tree_top_item.removeChild(item)
289
+
290
+ def add_tree_item(
291
+ self, service_name: str, status: BECStatus, info: dict = None, metrics: dict = None
292
+ ) -> None:
293
+ """Method to add a new QTreeWidgetItem together with a StatusItem to the tree widget.
294
+
295
+ Args:
296
+ service_name (str): The name of the service.
297
+ service_status_msg (StatusMessage): The status of the service.
298
+ metrics (dict): The metrics of the service.
299
+ """
300
+ item_widget = self._create_status_widget(
301
+ service_name=service_name, status=status, info=info, metrics=metrics
302
+ )
303
+ item = QTreeWidgetItem()
304
+ self.service_update.connect(item_widget.update_config)
305
+ self.tree_top_item.addChild(item)
306
+ self.setItemWidget(item, 0, item_widget)
307
+ self.tree_items.update({service_name: (item, item_widget)})
308
+
309
+ def init_ui_tree_widget(self) -> None:
310
+ """Initialise the tree widget for the status box."""
311
+ self.setHeaderHidden(True)
312
+ self.setStyleSheet(
313
+ "QTreeWidget::item:!selected "
314
+ "{ "
315
+ "border: 1px solid gainsboro; "
316
+ "border-left: none; "
317
+ "border-top: none; "
318
+ "}"
319
+ "QTreeWidget::item:selected {}"
320
+ )
321
+
322
+ @Slot(QTreeWidgetItem, int)
323
+ def on_tree_item_double_clicked(self, item: QTreeWidgetItem, column: int) -> None:
324
+ """Callback function for double clicks on individual QTreeWidgetItems in the collapsed section.
325
+
326
+ Args:
327
+ item (QTreeWidgetItem): The item that was double clicked.
328
+ column (int): The column that was double clicked.
329
+ """
330
+ for _, (tree_item, status_widget) in self.tree_items.items():
331
+ if tree_item == item:
332
+ status_widget.show_popup()
333
+
334
+ def closeEvent(self, event):
335
+ super().cleanup()
336
+ QTreeWidget().closeEvent(event)
337
+
338
+
339
+ def main():
340
+ """Main method to run the BECStatusBox widget."""
341
+ # pylint: disable=import-outside-toplevel
342
+ from qtpy.QtWidgets import QApplication
343
+
344
+ app = QApplication(sys.argv)
345
+ qdarktheme.setup_theme("auto")
346
+ main_window = BECStatusBox()
347
+ main_window.show()
348
+ sys.exit(app.exec())
349
+
350
+
351
+ if __name__ == "__main__":
352
+ main()
@@ -0,0 +1,171 @@
1
+ """ Module for a StatusItem widget to display status and metrics for a BEC service.
2
+ The widget is bound to be used with the BECStatusBox widget."""
3
+
4
+ import enum
5
+ import sys
6
+ from datetime import datetime
7
+
8
+ import qdarktheme
9
+ from bec_lib.utils.import_utils import lazy_import_from
10
+ from pydantic import Field
11
+ from qtpy.QtCore import Qt, Slot
12
+ from qtpy.QtWidgets import QDialog, QHBoxLayout, QLabel, QStyle, QVBoxLayout, QWidget
13
+
14
+ from bec_widgets.utils.bec_connector import ConnectionConfig
15
+
16
+ # TODO : Put normal imports back when Pydantic gets faster
17
+ BECStatus = lazy_import_from("bec_lib.messages", ("BECStatus",))
18
+
19
+
20
+ class IconsEnum(enum.Enum):
21
+ """Enum class for icons in the status item widget."""
22
+
23
+ RUNNING = "SP_DialogApplyButton"
24
+ BUSY = "SP_BrowserReload"
25
+ IDLE = "SP_MessageBoxWarning"
26
+ ERROR = "SP_DialogCancelButton"
27
+ NOTCONNECTED = "SP_TitleBarContextHelpButton"
28
+
29
+
30
+ class StatusWidgetConfig(ConnectionConfig):
31
+ """Configuration class for the status item widget."""
32
+
33
+ service_name: str
34
+ status: str
35
+ info: dict
36
+ metrics: dict | None
37
+ icon_size: tuple = Field(default=(24, 24), description="The size of the icon in the widget.")
38
+ font_size: int = Field(16, description="The font size of the text in the widget.")
39
+
40
+
41
+ class StatusItem(QWidget):
42
+ """A widget to display the status of a service.
43
+
44
+ Args:
45
+ parent: The parent widget.
46
+ config (dict): The configuration for the service.
47
+ """
48
+
49
+ def __init__(self, parent=None, config: dict = None):
50
+ if config is None:
51
+ config = StatusWidgetConfig(widget_class=self.__class__.__name__)
52
+ else:
53
+ if isinstance(config, dict):
54
+ config = StatusWidgetConfig(**config)
55
+ self.config = config
56
+ QWidget.__init__(self, parent=parent)
57
+ self.parent = parent
58
+ self.layout = None
59
+ self.config = config
60
+ self._popup_label_ref = {}
61
+ self._label = None
62
+ self._icon = None
63
+ self.init_ui()
64
+
65
+ def init_ui(self) -> None:
66
+ """Init the UI for the status item widget."""
67
+ self.layout = QHBoxLayout()
68
+ self.layout.setContentsMargins(5, 5, 5, 5)
69
+ self.setLayout(self.layout)
70
+ self._label = QLabel()
71
+ self._icon = QLabel()
72
+ self.layout.addWidget(self._label)
73
+ self.layout.addWidget(self._icon)
74
+ self.update_ui()
75
+
76
+ @Slot(dict)
77
+ def update_config(self, config: dict) -> None:
78
+ """Update the configuration of the status item widget.
79
+ This method is invoked from the parent widget.
80
+ The UI values are later updated based on the new configuration.
81
+
82
+ Args:
83
+ config (dict): Config updates from parent widget.
84
+ """
85
+ if config["service_name"] != self.config.service_name:
86
+ return
87
+ self.config.status = config["status"]
88
+ self.config.info = config["info"]
89
+ self.config.metrics = config["metrics"]
90
+ self.update_ui()
91
+
92
+ def update_ui(self) -> None:
93
+ """Update the UI of the labels, and popup dialog."""
94
+ self.set_text()
95
+ self.set_status()
96
+ self._set_popup_text()
97
+
98
+ def set_text(self) -> None:
99
+ """Set the text of the QLabel basae on the config."""
100
+ service = self.config.service_name
101
+ status = self.config.status
102
+ if "BECClient" in service.split("/"):
103
+ service = service.split("/")[0] + "/..." + service.split("/")[1][-4:]
104
+ if status == "NOTCONNECTED":
105
+ status = "NOT CONNECTED"
106
+ text = f"{service} is {status}"
107
+ self._label.setText(text)
108
+
109
+ def set_status(self) -> None:
110
+ """Set the status icon for the status item widget."""
111
+ icon_name = IconsEnum[self.config.status].value
112
+ icon = self.style().standardIcon(getattr(QStyle.StandardPixmap, icon_name))
113
+ self._icon.setPixmap(icon.pixmap(*self.config.icon_size))
114
+ self._icon.setAlignment(Qt.AlignmentFlag.AlignRight)
115
+
116
+ def show_popup(self) -> None:
117
+ """Method that is invoked when the user double clicks on the StatusItem widget."""
118
+ dialog = QDialog(self)
119
+ dialog.setWindowTitle(f"{self.config.service_name} Details")
120
+ layout = QVBoxLayout()
121
+ popup_label = self._make_popup_label()
122
+ self._set_popup_text()
123
+ layout.addWidget(popup_label)
124
+ dialog.setLayout(layout)
125
+ dialog.finished.connect(self._cleanup_popup_label)
126
+ dialog.exec()
127
+
128
+ def _make_popup_label(self) -> QLabel:
129
+ """Create a QLabel for the popup dialog.
130
+
131
+ Returns:
132
+ QLabel: The label for the popup dialog.
133
+ """
134
+ label = QLabel()
135
+ label.setWordWrap(True)
136
+ self._popup_label_ref.update({"label": label})
137
+ return label
138
+
139
+ def _set_popup_text(self) -> None:
140
+ """Compile the metrics text for the status item widget."""
141
+ if self._popup_label_ref.get("label") is None:
142
+ return
143
+ metrics_text = (
144
+ f"<b>SERVICE:</b> {self.config.service_name}<br><b>STATUS:</b> {self.config.status}<br>"
145
+ )
146
+ if self.config.metrics:
147
+ for key, value in self.config.metrics.items():
148
+ if key == "create_time":
149
+ value = datetime.fromtimestamp(value).strftime("%Y-%m-%d %H:%M:%S")
150
+ metrics_text += f"<b>{key.upper()}:</b> {value}<br>"
151
+ self._popup_label_ref["label"].setText(metrics_text)
152
+
153
+ def _cleanup_popup_label(self) -> None:
154
+ """Cleanup the popup label."""
155
+ self._popup_label_ref.clear()
156
+
157
+
158
+ def main():
159
+ """Run the status item widget."""
160
+ # pylint: disable=import-outside-toplevel
161
+ from qtpy.QtWidgets import QApplication
162
+
163
+ app = QApplication(sys.argv)
164
+ qdarktheme.setup_theme("auto")
165
+ main_window = StatusItem()
166
+ main_window.show()
167
+ sys.exit(app.exec())
168
+
169
+
170
+ if __name__ == "__main__":
171
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.66.1
3
+ Version: 0.68.0
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
@@ -2,11 +2,11 @@
2
2
  .gitlab-ci.yml,sha256=RnYDz4zKXjlqltTryprlB1s5vLXxI2-seW-Vb70NNF0,8162
3
3
  .pylintrc,sha256=OstrgmEyP0smNFBKoIN5_26-UmNZgMHnbjvAWX0UrLs,18535
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=Vh2_soSc85dy9lRU01vmqFhnlnY81lRRHA67ib_xlto,6911
5
+ CHANGELOG.md,sha256=EhWy5RDQRny8SHputsNnGM7Hy1HOZoSmZ6cJB2Gw8Cw,7198
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=u-mGylcSYfdLA6I276ETlul9ZXRitI9ECVKvIBxHY8w,1302
7
+ PKG-INFO,sha256=I5LEt7gWZyuBZpnkwiIXlKyxz5sQOmtI-RAIDZjzD9s,1302
8
8
  README.md,sha256=y4jB6wvArS7N8_iTbKWnSM_oRAqLA2GqgzUR-FMh5sU,2645
9
- pyproject.toml,sha256=6z3-hs4UXP5ysKDuF4Te-9abra68AXuIvGB3ReAukC4,2162
9
+ pyproject.toml,sha256=wCSTnyvyFrQnQiDgC5Pn18_zeLOguN86bLfJVSIboz8,2162
10
10
  .git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
11
11
  .gitlab/issue_templates/bug_report_template.md,sha256=gAuyEwl7XlnebBrkiJ9AqffSNOywmr8vygUFWKTuQeI,386
12
12
  .gitlab/issue_templates/documentation_update_template.md,sha256=FHLdb3TS_D9aL4CYZCjyXSulbaW5mrN2CmwTaeLPbNw,860
@@ -17,12 +17,12 @@ bec_widgets/assets/bec_widgets_icon.png,sha256=K8dgGwIjalDh9PRHUsSQBqgdX7a00nM3i
17
17
  bec_widgets/assets/terminal_icon.png,sha256=bJl7Tft4Fi2uxvuXI8o14uMHnI9eAWKSU2uftXCH9ws,3889
18
18
  bec_widgets/cli/__init__.py,sha256=d0Q6Fn44e7wFfLabDOBxpcJ1DPKWlFunGYDUBmO-4hA,22
19
19
  bec_widgets/cli/auto_updates.py,sha256=DyBV3HnjMSH-cvVkYNcDiYKVf0Xut4Qy2qGQqkW47Bw,4833
20
- bec_widgets/cli/client.py,sha256=GJRzys0DVHQp8X9QxociTrshPm4CvaPtEprxWEpEKCs,56446
21
- bec_widgets/cli/client_utils.py,sha256=D076XKwcukKBKknd11B1UyOcQN_9sN7ZKMVttyCxS9Q,11586
20
+ bec_widgets/cli/client.py,sha256=Zd4oMSE5-HY3IBUIVcparGGV2Ew86gaWAFTd4OjFVmg,58005
21
+ bec_widgets/cli/client_utils.py,sha256=_Hb2nl1rKEf7k4By9VZDYl5YyGFczxMuYIFMVrOAZD0,12182
22
22
  bec_widgets/cli/generate_cli.py,sha256=Bi8HxHhge1I87vbdYHZUZiZwvbB-OSkLYS5Xfmwiz9M,4922
23
23
  bec_widgets/cli/rpc_register.py,sha256=QxXUZu5XNg00Yf5O3UHWOXg3-f_pzKjjoZYMOa-MOJc,2216
24
24
  bec_widgets/cli/rpc_wigdet_handler.py,sha256=1qQOGrM8rozaWLkoxAW8DTVLv_L_DZdZgUMDPy5MOek,1486
25
- bec_widgets/cli/server.py,sha256=4sigviIyJgZOgikWHc1X998vWAWayKF6S61oAY_mVDQ,5727
25
+ bec_widgets/cli/server.py,sha256=3bFBPmtXKXFMjeja18d0hF3CO66Jo0-LEDtcF7lYb7k,7166
26
26
  bec_widgets/examples/__init__.py,sha256=WWQ0cu7m8sA4Ehy-DWdTIqSISjaHsbxhsNmNrMnhDZU,202
27
27
  bec_widgets/examples/jupyter_console/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  bec_widgets/examples/jupyter_console/jupyter_console_window.py,sha256=FXf0q7oz9GyJSct8PAgeOalzNnIJjApiaRvNfXsZPs0,5345
@@ -32,7 +32,7 @@ bec_widgets/examples/motor_movement/motor_control_compilations.py,sha256=8rpA7a2
32
32
  bec_widgets/examples/motor_movement/motor_controller.ui,sha256=83XX6NGILwntoUIghvzWnMuGf80O8khK3SduVKTAEFM,29105
33
33
  bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
34
34
  bec_widgets/utils/bec_connector.py,sha256=RxHJNF7JjtY5pRbTMu2eQTiRXvoyJ53QuTYxHjZba38,5357
35
- bec_widgets/utils/bec_dispatcher.py,sha256=vvHpD_7ZddRmD6mHR5JWU_XEJYxognpmG7CnEWMjaZk,5989
35
+ bec_widgets/utils/bec_dispatcher.py,sha256=yM9PG04O7ABhiA9Nzk38Rv9Qbjc5O93wi2xfSbOlOxc,6202
36
36
  bec_widgets/utils/bec_table.py,sha256=nA2b8ukSeUfquFMAxGrUVOqdrzMoDYD6O_4EYbOG2zk,717
37
37
  bec_widgets/utils/colors.py,sha256=GYSDe0ZxsJSwxvuy-yG2BH17qlf_Sjq8dhDcyp9IhBI,8532
38
38
  bec_widgets/utils/container_utils.py,sha256=m3VUyAYmSWkEwApP9tBvKxPYVtc2kHw4toxIpMryJy4,1495
@@ -47,6 +47,9 @@ bec_widgets/utils/validator_delegate.py,sha256=Emj1WF6W8Ke1ruBWUfmHdVJpmOSPezuOt
47
47
  bec_widgets/utils/widget_io.py,sha256=f36198CvT_EzWQ_cg2G-4tRRsaMdJ3yVqsZWKJCQEfA,10880
48
48
  bec_widgets/utils/yaml_dialog.py,sha256=cMVif-39SB9WjwGH5FWBJcFs4tnfFJFs5cacydRyhy0,1853
49
49
  bec_widgets/widgets/__init__.py,sha256=6RE9Pot2ud6BNJc_ZKiE--U-lgVRUff2IVR91lPcCbo,214
50
+ bec_widgets/widgets/bec_status_box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
+ bec_widgets/widgets/bec_status_box/bec_status_box.py,sha256=ARdVzwabmbT_Jti6Wp-5KrxDQIingYVC-pZQHd6uz7o,14375
52
+ bec_widgets/widgets/bec_status_box/status_item.py,sha256=wPkDm0GCGNXXpy3rR_Ljaxy0ZHeiiYcrWFqEntZnz4E,5869
50
53
  bec_widgets/widgets/buttons/__init__.py,sha256=74ucIRU6-anoqQ-zT7wbrysmxhg_3_04xGhN_kllNUI,48
51
54
  bec_widgets/widgets/buttons/stop_button/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
55
  bec_widgets/widgets/buttons/stop_button/stop_button.py,sha256=x4a7RvlMkHzOd05zKOGYkyTmBza7Me7jgOL9WIgA_c4,906
@@ -103,7 +106,7 @@ docs/Makefile,sha256=i2WHuFlgfyAPEW4ssEP8NY4cOibDJrVjvzSEU8_Ggwc,634
103
106
  docs/conf.py,sha256=HxLxupNGu0Smhwn57g1kFdjZzFuaWVREgRJKhT1zi2k,2464
104
107
  docs/index.md,sha256=8ZCgaLIbJsYvt-jwi--QxsNwnK4-k3rejIeOOLclG40,1101
105
108
  docs/make.bat,sha256=vKazJE8RW49Cy8K7hm8QYbletvAd8YkeKsaPA_dWnXs,800
106
- docs/requirements.txt,sha256=SbcWrLYX6tEZNMCUETgsE50Y7VioR1VX6YCeifuJWEc,140
109
+ docs/requirements.txt,sha256=TcjRnzVYFOg64N-lJc2sn-G6YQdhPjzp0lrLuPsPMXY,156
107
110
  docs/_static/custom.css,sha256=v4Nk7r8LZslhOV8RaSUb15bG4miwiZ4-kZyXBLnSyms,13487
108
111
  docs/_templates/custom-class-template.rst,sha256=HPuPaGJob2zXlWOl5FmA-hAZRbUTGQmdqo3HS1iIFog,711
109
112
  docs/_templates/custom-module-template.rst,sha256=MXYXAz06HP_mbblO--iFwL08xROmSBo7U4O-hPbMcZU,1228
@@ -118,7 +121,7 @@ docs/assets/rocket_launch_48dp.svg,sha256=pdrPrBcKWUa5OlgWKM0B6TA6qAW7E57d7C7YW2
118
121
  docs/developer/developer.md,sha256=VUdMnQBsSRCawYMFCe0vya5oj1MimLML7Pd58kY6fYY,765
119
122
  docs/developer/getting_started/development.md,sha256=aYLmuLMYpp5FcIXeDUqCfcStIV8veuiMBjOt5bTW_30,1406
120
123
  docs/developer/getting_started/getting_started.md,sha256=My_K_6O7LLaXVB_eINrRku5o-jVx95lsmGgHxgZhT7A,378
121
- docs/developer/widgets/widgets.md,sha256=O7v0DsgCr-IULxl0TJ7NIGN68wd5kouKz1Y5ZuEvaEU,529
124
+ docs/developer/widgets/widgets.md,sha256=aNsJgG7R-3EerumNB6GH84JLIXfZqGN5GjvpKWDi0Hk,504
122
125
  docs/introduction/introduction.md,sha256=wp7jmhkUtJnSnEnmIAZGUcau_3-5e5-FohvZb63khw4,1432
123
126
  docs/user/customisation.md,sha256=wCW8fAbqtlgGE3mURvXOrK67Xo0_B-lxfg0sYuQWB40,3186
124
127
  docs/user/user.md,sha256=uCTcjclIi6rdjYRQebko6bWFEVsjyfshsVU3BDYrC-Y,1403
@@ -128,11 +131,13 @@ docs/user/getting_started/BECDockArea.png,sha256=t3vSm_rVRk371J5LOutbolETuEjStNc
128
131
  docs/user/getting_started/auto_updates.md,sha256=Gicx3lplI6JRBlnPj_VL6IhqOIcsWjYF4_EdZSCje2A,3754
129
132
  docs/user/getting_started/getting_started.md,sha256=lxZXCr6HAkM61oo5Bu-YjINSKo4wihWhAPJdotEAAVQ,358
130
133
  docs/user/getting_started/gui_complex_gui.gif,sha256=ovv9u371BGG5GqhzyBMl4mvqMHLfJS0ylr-dR0Ydwtw,6550393
131
- docs/user/getting_started/installation.md,sha256=gqMV44lh9-wkKtAtDckvnyX_d8oTBNinQxvriFQ9Sk4,1145
134
+ docs/user/getting_started/installation.md,sha256=5_fPbmUqLGtwOskFHTlytd4PJKrMcHqHShzM9ymM0oI,1149
132
135
  docs/user/getting_started/quick_start.md,sha256=VGU880GwamcIZcBE8tjxuqX2syE-71jqZedtskCoBbA,9405
133
136
  docs/user/widgets/BECFigure.png,sha256=8dQr4u0uk_y0VV-R1Jh9yTR3Vidd9HDEno_07R0swaE,1605920
134
137
  docs/user/widgets/bec_figure.md,sha256=BwcumbhZd6a2zKmoHTvwKr8kG8WxBx9lS_QwxNiBMpQ,5155
135
- docs/user/widgets/buttons.md,sha256=LG-Csj9RL7hWur8Xgj19r-u2SuIFq912fyBVN6peLGY,1222
138
+ docs/user/widgets/bec_status_box.gif,sha256=kLxf40HbS6fjdUIQ2b9SiduBEXdBd4DDWGEnQDOFMcY,1259044
139
+ docs/user/widgets/bec_status_box.md,sha256=0ILY12UnSjiVCtd5qpC8G2dPBYhug3J_rmQLilDxulY,1164
140
+ docs/user/widgets/buttons.md,sha256=Yci21PmxlRvfKcrvY7mVI7JkECPmE6j9WyWyH3j7Y2o,1221
136
141
  docs/user/widgets/image_plot.gif,sha256=_mVFhMTXGqwDOcEtrBHMZj5Thn2sLhDAHEeL2XyHN-s,14098977
137
142
  docs/user/widgets/motor.gif,sha256=FtaWdRHx4UZaGJPpq8LNhMMgX4PFcAB6IZ93JCMEh_w,2280719
138
143
  docs/user/widgets/progress_bar.gif,sha256=5jh0Zw2BBGPuNxszV1DBLJCb4_6glIRX-U2ABjnsK2k,5263592
@@ -141,10 +146,10 @@ docs/user/widgets/spiral_progress_bar.md,sha256=QTgUDIl6XPuK_HwSfB6sNijZ4bss26bi
141
146
  docs/user/widgets/text_box.md,sha256=_ST7RQWXl67MKLm6dTa995GjoughPUyK_hLnF8SPZcM,925
142
147
  docs/user/widgets/w1D.gif,sha256=tuHbleJpl6bJFNNC2OdndF5LF7IyfvlkFCMGZajrQPs,622773
143
148
  docs/user/widgets/website.md,sha256=wfudAupdtHX-Sfritg0xMWXZLLczJ4XwMLNWvu6ww-w,705
144
- docs/user/widgets/widgets.md,sha256=NzRfrgd4LWmZHa2Cs_1G59LeY5uAlFdy5aP00AtGAjk,380
149
+ docs/user/widgets/widgets.md,sha256=6H8C8M2fFmTxFFlrAuOE-jBpOUVUrOIIzWP0l_EwMGo,397
145
150
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
146
151
  tests/end-2-end/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
- tests/end-2-end/conftest.py,sha256=j1O1SxXRJ8jcrunn6dcfbZLK2Jc-VUxyh9ZuCSc6Qj4,1816
152
+ tests/end-2-end/conftest.py,sha256=-BLnFE-NeCerf6xahGCkbZ4Ktactowi6RkBnboIzRvg,1767
148
153
  tests/end-2-end/test_bec_dock_rpc_e2e.py,sha256=8iJz4lITspY7eHdSgy9YvGUGTu3fsSperoVGBvTGT0U,9067
149
154
  tests/end-2-end/test_bec_figure_rpc_e2e.py,sha256=zTbB_F4Fs-QG8KhMK24xfsrCQBgZUAguMk3KFdEdP2o,5095
150
155
  tests/end-2-end/test_rpc_register_e2e.py,sha256=3dfCnSvdcRO92pzHt9WlCTK0vzTKAvPtliEoEKrtuzQ,1604
@@ -156,6 +161,7 @@ tests/unit_tests/test_bec_dispatcher.py,sha256=rYPiRizHaswhGZw55IBMneDFxmPiCCLAZ
156
161
  tests/unit_tests/test_bec_dock.py,sha256=BXKXpuyIYj-l6KSyhQtM_p3kRFCRECIoXLzvkcJZDlM,3611
157
162
  tests/unit_tests/test_bec_figure.py,sha256=aEd2R8K6fU2ON8QvPemGWpql_LaaYLipRlvnjBY2qFA,8009
158
163
  tests/unit_tests/test_bec_motor_map.py,sha256=AfD_9-x6VV3TPnkQgNfFYRndPHDsGx-a_YknFeDr6hc,4588
164
+ tests/unit_tests/test_bec_status_box.py,sha256=zZ4pe7DaBzzpRsy62yHFkUGgAGb3zZU3I6zQIPsqUTY,6070
159
165
  tests/unit_tests/test_client_utils.py,sha256=eViJ1Tz-HX9TkMvQH6W8cO-c3_1I8bUc4_Yen6LOc0E,830
160
166
  tests/unit_tests/test_color_validation.py,sha256=csdvVKAohENZIRY-JQ97Hv-TShb1erj4oKMX7QRwo78,1883
161
167
  tests/unit_tests/test_crosshair.py,sha256=3OMAJ2ZaISYXMOtkXf1rPdy94vCr8njeLi6uHblBL9Q,5045
@@ -180,8 +186,8 @@ tests/unit_tests/test_configs/config_device_no_entry.yaml,sha256=hdvue9KLc_kfNzG
180
186
  tests/unit_tests/test_configs/config_scan.yaml,sha256=vo484BbWOjA_e-h6bTjSV9k7QaQHrlAvx-z8wtY-P4E,1915
181
187
  tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
182
188
  tests/unit_tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
183
- bec_widgets-0.66.1.dist-info/METADATA,sha256=u-mGylcSYfdLA6I276ETlul9ZXRitI9ECVKvIBxHY8w,1302
184
- bec_widgets-0.66.1.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
185
- bec_widgets-0.66.1.dist-info/entry_points.txt,sha256=OvoqiNzNF9bizFQNhbAmmdc_njHrnVewLE-Kl-u9sh0,115
186
- bec_widgets-0.66.1.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
187
- bec_widgets-0.66.1.dist-info/RECORD,,
189
+ bec_widgets-0.68.0.dist-info/METADATA,sha256=I5LEt7gWZyuBZpnkwiIXlKyxz5sQOmtI-RAIDZjzD9s,1302
190
+ bec_widgets-0.68.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
191
+ bec_widgets-0.68.0.dist-info/entry_points.txt,sha256=OvoqiNzNF9bizFQNhbAmmdc_njHrnVewLE-Kl-u9sh0,115
192
+ bec_widgets-0.68.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
193
+ bec_widgets-0.68.0.dist-info/RECORD,,
@@ -8,5 +8,4 @@ maxdepth: 2
8
8
  hidden: false
9
9
  ---
10
10
 
11
- how_to_develop_a_widget/
12
11
  ```
docs/requirements.txt CHANGED
@@ -7,5 +7,6 @@ sphinx-copybutton
7
7
  myst-parser
8
8
  sphinx-design
9
9
  PyQt6
10
+ PyQt6-WebEngine
10
11
  bec-widgets
11
12
  tomli