diff --git a/bec_widgets/widgets/bec_status_box/bec_status_box.py b/bec_widgets/widgets/bec_status_box/bec_status_box.py index a3c0277e..a442bcdb 100644 --- a/bec_widgets/widgets/bec_status_box/bec_status_box.py +++ b/bec_widgets/widgets/bec_status_box/bec_status_box.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING import qdarktheme from bec_lib.utils.import_utils import lazy_import_from from qtpy.QtCore import QObject, QTimer, Signal, Slot -from qtpy.QtWidgets import QTreeWidget, QTreeWidgetItem +from qtpy.QtWidgets import QHBoxLayout, QTreeWidget, QTreeWidgetItem, QWidget from bec_widgets.utils.bec_connector import BECConnector from bec_widgets.widgets.bec_status_box.status_item import StatusItem @@ -43,8 +43,8 @@ class BECServiceStatusMixin(QObject): services_update = Signal(dict, dict) - def __init__(self, client: BECClient): - super().__init__() + def __init__(self, parent, client: BECClient): + super().__init__(parent) self.client = client self._service_update_timer = QTimer() self._service_update_timer.timeout.connect(self._get_service_status) @@ -57,7 +57,7 @@ class BECServiceStatusMixin(QObject): self.services_update.emit(self.client._services_info, self.client._services_metric) -class BECStatusBox(BECConnector, QTreeWidget): +class BECStatusBox(BECConnector, QWidget): """An autonomous widget to display the status of BEC services. Args: @@ -73,9 +73,6 @@ class BECStatusBox(BECConnector, QTreeWidget): service_update = Signal(BECServiceInfoContainer) bec_core_state = Signal(str) - _initialized = False - _bec_status_box = None - def __init__( self, parent=None, @@ -84,34 +81,23 @@ class BECStatusBox(BECConnector, QTreeWidget): bec_service_status_mixin: BECServiceStatusMixin = None, gui_id: str = None, ): - if self._initialized == True: - return super().__init__(client=client, gui_id=gui_id) - QTreeWidget.__init__(self, parent=parent) + QWidget.__init__(self, parent=parent) + self.tree = QTreeWidget(self) + self.layout = QHBoxLayout(self) self.box_name = box_name self.status_container = defaultdict(lambda: {"info": None, "item": None, "widget": None}) - self._initialized = False if not bec_service_status_mixin: - bec_service_status_mixin = BECServiceStatusMixin(client=self.client) + bec_service_status_mixin = BECServiceStatusMixin(self, client=self.client) self.bec_service_status = bec_service_status_mixin - if not self._initialized: - self.init_ui() - self.bec_service_status.services_update.connect(self.update_service_status) - self.bec_core_state.connect(self.update_top_item_status) - self.itemDoubleClicked.connect(self.on_tree_item_double_clicked) - - def __new__(cls, *args, forced: bool = False, **kwargs): - if forced: - cls._initialized = False - cls._bec_status_box = super(BECStatusBox, cls).__new__(cls) - return cls._bec_status_box - if cls._bec_status_box is not None and cls._initialized is True: - return cls._bec_status_box - cls._bec_status_box = super(BECStatusBox, cls).__new__(cls) - return cls._bec_status_box + self.init_ui() + self.bec_service_status.services_update.connect(self.update_service_status) + self.bec_core_state.connect(self.update_top_item_status) + self.tree.itemDoubleClicked.connect(self.on_tree_item_double_clicked) + self.layout.addWidget(self.tree) def init_ui(self) -> None: """Init the UI for the BECStatusBox widget, should only take place once.""" @@ -121,15 +107,16 @@ class BECStatusBox(BECConnector, QTreeWidget): tree_item.setExpanded(True) tree_item.setDisabled(True) self.status_container[self.box_name].update({"item": tree_item, "widget": top_label}) - self.addTopLevelItem(tree_item) - self.setItemWidget(tree_item, 0, top_label) + self.tree.addTopLevelItem(tree_item) + self.tree.setItemWidget(tree_item, 0, top_label) self.service_update.connect(top_label.update_config) self._initialized = True def init_ui_tree_widget(self) -> None: """Initialise the tree widget for the status box.""" - self.setHeaderHidden(True) - self.setStyleSheet( + self.tree.setHeaderHidden(True) + # TODO probably here is a problem still with setting the stylesheet + self.tree.setStyleSheet( "QTreeWidget::item:!selected " "{ " "border: 1px solid gainsboro; " @@ -191,7 +178,10 @@ class BECStatusBox(BECConnector, QTreeWidget): container.metrics = metrics return service_info_item = BECServiceInfoContainer( - service_name=service_name, status=status.name, info=info, metrics=metrics + service_name=service_name, + status=status.name if isinstance(status, BECStatus) else status, + info=info, + metrics=metrics, ) self.status_container[service_name].update({"info": service_info_item}) @@ -213,6 +203,9 @@ class BECStatusBox(BECConnector, QTreeWidget): metric_msg = services_metric.get(service_name, None) metrics = metric_msg.metrics if metric_msg else None if service_name in self.status_container: + if not msg: + self.add_tree_item(service_name, "NOTCONNECTED", {}, metrics) + continue self._update_status_container(service_name, msg.status, msg.info, metrics) self.service_update.emit(self.status_container[service_name]["info"]) continue @@ -236,6 +229,9 @@ class BECStatusBox(BECConnector, QTreeWidget): metrics = metric_msg.metrics if metric_msg else None msg = services_info.pop(service_name, None) if service_name not in self.status_container: + if not msg: + self.add_tree_item(service_name, "NOTCONNECTED", {}, metrics) + continue self.add_tree_item(service_name, msg.status, msg.info, metrics) continue if not msg: @@ -248,8 +244,6 @@ class BECStatusBox(BECConnector, QTreeWidget): self.service_update.emit(self.status_container[service_name]["info"]) - # self.add_tree_item(service_name, msg.status, msg.info, metrics) - self.bec_core_state.emit(core_state.name if core_state else "NOTCONNECTED") return services_info @@ -278,10 +272,10 @@ class BECStatusBox(BECConnector, QTreeWidget): metrics (dict): The metrics of the service. """ item_widget = self._create_status_widget(service_name, status, info, metrics) - item = QTreeWidgetItem() # setDisabled=True + item = QTreeWidgetItem() self.service_update.connect(item_widget.update_config) self.status_container[self.box_name]["item"].addChild(item) - self.setItemWidget(item, 0, item_widget) + self.tree.setItemWidget(item, 0, item_widget) self.status_container[service_name].update({"item": item, "widget": item_widget}) @Slot(QTreeWidgetItem, int) @@ -297,13 +291,13 @@ class BECStatusBox(BECConnector, QTreeWidget): objects["widget"].show_popup() def closeEvent(self, event): - """Upon closing the widget, clean up the BECStatusBox and the QTreeWidget. + """Upon closing the widget, clean up the BECStatusBox and the QWidget. Args: event: The close event. """ super().cleanup() - return QTreeWidget.closeEvent(self, event) + super().closeEvent(event) def main(): diff --git a/bec_widgets/widgets/bec_status_box/bec_status_box_plugin.py b/bec_widgets/widgets/bec_status_box/bec_status_box_plugin.py index 5d979ac1..97690dc2 100644 --- a/bec_widgets/widgets/bec_status_box/bec_status_box_plugin.py +++ b/bec_widgets/widgets/bec_status_box/bec_status_box_plugin.py @@ -48,7 +48,7 @@ class BECStatusBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover return "BECStatusBox" def toolTip(self): - return "Widget to display the BECStatus from all active services." + return "An autonomous widget to display the status of BEC services." def whatsThis(self): return self.toolTip() diff --git a/tests/unit_tests/test_bec_status_box.py b/tests/unit_tests/test_bec_status_box.py index c58a8904..164b83c7 100644 --- a/tests/unit_tests/test_bec_status_box.py +++ b/tests/unit_tests/test_bec_status_box.py @@ -23,11 +23,11 @@ def status_box(qtbot, mocked_client, service_status_fixture): def test_update_top_item(status_box): - assert status_box.children()[0].children()[0].config.status == "IDLE" + assert status_box.tree.children()[0].children()[0].config.status == "IDLE" name = status_box.box_name status_box.update_top_item_status(status="RUNNING") assert status_box.status_container[name]["info"].status == "RUNNING" - assert status_box.children()[0].children()[0].config.status == "RUNNING" + assert status_box.tree.children()[0].children()[0].config.status == "RUNNING" def test_create_status_widget(status_box): @@ -62,9 +62,9 @@ def test_add_tree_item(status_box): status = BECStatus.IDLE info = {"test": "test"} metrics = {"metric": "test_metric"} - assert len(status_box.children()[0].children()) == 1 + assert len(status_box.tree.children()[0].children()) == 1 status_box.add_tree_item(name, status, info, metrics) - assert len(status_box.children()[0].children()) == 2 + assert len(status_box.tree.children()[0].children()) == 2 assert name in status_box.status_container @@ -98,14 +98,14 @@ def test_update_core_services(status_box): services_metrics = {name: ServiceMetricMessage(name=name, metrics=metrics)} status_box.update_core_services(services_status, services_metrics) - assert status_box.children()[0].children()[0].config.status == "RUNNING" + assert status_box.tree.children()[0].children()[0].config.status == "RUNNING" assert status_box.status_container[name]["widget"].config.metrics == metrics status = BECStatus.IDLE services_status = {name: StatusMessage(name=name, status=status, info=info)} services_metrics = {name: ServiceMetricMessage(name=name, metrics=metrics)} status_box.update_core_services(services_status, services_metrics) - assert status_box.children()[0].children()[0].config.status == status.name + assert status_box.tree.children()[0].children()[0].config.status == status.name assert status_box.status_container[name]["widget"].config.metrics == metrics @@ -119,5 +119,5 @@ def test_double_click_item(status_box): item = container["item"] status_item = container["widget"] with mock.patch.object(status_item, "show_popup") as mock_show_popup: - status_box.itemDoubleClicked.emit(item, 0) + status_box.tree.itemDoubleClicked.emit(item, 0) assert mock_show_popup.call_count == 1