diff --git a/bec_widgets/widgets/control/procedure_control/procedure_control.py b/bec_widgets/widgets/control/procedure_control/procedure_control.py index 3b002242..dc0c53b2 100644 --- a/bec_widgets/widgets/control/procedure_control/procedure_control.py +++ b/bec_widgets/widgets/control/procedure_control/procedure_control.py @@ -12,6 +12,7 @@ from bec_lib.messages import ( from bec_qthemes._icon.material_icons import material_icon from bec_server.scan_server.procedures.helper import FrontendProcedureHelper from pydantic import BaseModel, ConfigDict +from qtpy.QtCore import Signal from qtpy.QtWidgets import ( QHBoxLayout, QToolButton, @@ -107,6 +108,9 @@ class JobItem(_ActionItem): self._msg = config.msg self._init_params_display() + def queue(self): + return self._msg.queue + def _init_params_display(self): self.setText(self._config.params_column, self._short_params_text()) self.setToolTip(self._config.params_column, self._long_params_html()) @@ -178,6 +182,9 @@ class QueueItem(_ActionItem): _ItemConfig(base=self._config, msg=msg), ) + def queue(self): + return self._queue + @SafeSlot() def _abort_self(self): self._config.helper.request.abort_queue(self._queue) @@ -209,6 +216,8 @@ class CategoryItem(QTreeWidgetItem): class ProcedureControl(BECWidget, QWidget): + queue_selected = Signal(str) + def __init__(self, parent=None, client=None, config=None, gui_id: str | None = None, **kwargs): config = config or ConnectionConfig() super().__init__(parent=parent, client=client, config=config, gui_id=gui_id, **kwargs) @@ -217,6 +226,17 @@ class ProcedureControl(BECWidget, QWidget): self._setup_ui() self.bec_dispatcher.connect_slot(self._update, MessageEndpoints.procedure_queue_notif()) self._init_queues() + self._content.itemSelectionChanged.connect(self.on_selection_changed) + + def on_selection_changed(self): + selected_items = self._content.selectedItems() + if len(selected_items) != 1: + self.queue_selected.emit("") + return + if isinstance((item := selected_items[0]), (QueueItem, JobItem)): + self.queue_selected.emit(item.queue()) + return + self.queue_selected.emit("") @SafeSlot(ProcedureQNotifMessage, dict) def _update(self, msg: dict | ProcedureQNotifMessage, _): @@ -237,7 +257,6 @@ class ProcedureControl(BECWidget, QWidget): self._content.setAlternatingRowColors(True) self._content.setHeaderLabels(["name", "status", "params", "actions"]) self._layout.addWidget(self._content) - self._content.header().resizeSection(0, 250) config = partial(_BaseConfig, helper=self._helper, tree=self._content, actions_column=3) diff --git a/bec_widgets/widgets/control/procedure_control/procedure_logs.py b/bec_widgets/widgets/control/procedure_control/procedure_logs.py index 2c3e48e1..b82be468 100644 --- a/bec_widgets/widgets/control/procedure_control/procedure_logs.py +++ b/bec_widgets/widgets/control/procedure_control/procedure_logs.py @@ -32,15 +32,15 @@ class ProcedureLogs(BECWidget, QWidget): self._layout.addWidget(self.widget) @SafeSlot(dict, dict) - def _trigger_update(self, msg, _): + def _update(self, msg, _): self.widget.append(msg.get("data").strip()) - def _update(self): + def _init_content(self): if self._queue is None: self.widget.setText("") return - if msgs := self._conn.xread(MessageEndpoints.procedure_logs(self._queue)): - self.widget.append("".join(msg.get("data").data.strip() for msg in msgs)) + if msgs := self._conn.xread(MessageEndpoints.procedure_logs(self._queue), from_start=True): + self.widget.append("\n".join(msg.get("data").data.strip() for msg in msgs)) @SafeSlot(None) @SafeSlot(str) @@ -53,16 +53,18 @@ class ProcedureLogs(BECWidget, QWidget): @queue.setter def queue(self, queue: str | None) -> None: + if self._queue == queue: + return if self._queue is not None: self.bec_dispatcher.disconnect_slot( - self._trigger_update, MessageEndpoints.procedure_logs(self._queue) + self._update, MessageEndpoints.procedure_logs(self._queue) ) - self._queue = queue + self._queue = queue or None if self._queue is not None: self.bec_dispatcher.connect_slot( - self._trigger_update, MessageEndpoints.procedure_logs(self._queue) + self._update, MessageEndpoints.procedure_logs(self._queue) ) - self._update() + self._init_content() if __name__ == "__main__": diff --git a/bec_widgets/widgets/control/procedure_control/procedure_panel.py b/bec_widgets/widgets/control/procedure_control/procedure_panel.py new file mode 100644 index 00000000..9d66d7d4 --- /dev/null +++ b/bec_widgets/widgets/control/procedure_control/procedure_panel.py @@ -0,0 +1,59 @@ +from qtpy.QtWidgets import QVBoxLayout, QWidget + +from bec_widgets import BECWidget +from bec_widgets.utils.toolbars.toolbar import ModularToolBar +from bec_widgets.widgets.containers.ads import CDockManager, CDockWidget, DockWidgetArea +from bec_widgets.widgets.containers.advanced_dock_area.advanced_dock_area import AdvancedDockArea +from bec_widgets.widgets.control.procedure_control.procedure_control import ProcedureControl +from bec_widgets.widgets.control.procedure_control.procedure_logs import ProcedureLogs + + +class ProcedurePanel(BECWidget, QWidget): + + def __init__(self, parent=None, **kwargs): + super().__init__(parent=parent, **kwargs) + + # Top-level layout hosting a toolbar and the dock manager + self._root_layout = QVBoxLayout(self) + self._root_layout.setContentsMargins(0, 0, 0, 0) + self._root_layout.setSpacing(0) + self.toolbar = ModularToolBar(self) + self._root_layout.addWidget(self.toolbar) + + self.dock_manager = CDockManager(self) + self.dock_manager.setStyleSheet("") + self._root_layout.addWidget(self.dock_manager) + + self.procedure_control = ProcedureControl(parent=self) + self.procedure_logs = ProcedureLogs(parent=self) + + self.procedure_control_dock = CDockWidget("Procedure Control", self) + self.procedure_control_dock.setWidget(self.procedure_control) + + self.procedure_logs_dock = CDockWidget("Procedure Logs", self) + self.procedure_logs_dock.setWidget(self.procedure_logs) + + self.dock_manager.addDockWidget( + DockWidgetArea.TopDockWidgetArea, self.procedure_control_dock + ) + self.dock_manager.addDockWidget( + DockWidgetArea.BottomDockWidgetArea, self.procedure_logs_dock + ) + + for dock in self.dock_manager.dockWidgets(): + dock.setFeature(CDockWidget.DockWidgetFeature.DockWidgetClosable, False) + dock.setFeature(CDockWidget.DockWidgetFeature.DockWidgetFloatable, True) + dock.setFeature(CDockWidget.DockWidgetFeature.DockWidgetMovable, False) + + self.procedure_control.queue_selected.connect(self.procedure_logs.set_queue) + + +if __name__ == "__main__": + import sys + + from qtpy.QtWidgets import QApplication + + app = QApplication(sys.argv) + widget = ProcedurePanel() + widget.show() + sys.exit(app.exec())