feat(dock_area): add QWidget scan queue next to the QML one in the Add Utils toolbar

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F2iSCsSSznEdY4HxoVaqoi
This commit is contained in:
Claude committed 2026-10-12 11:30:21 +00:00
1 parent 018e5051b7
commit 5dd886d130
9 files changed
+124 -4

No files matched your search

+25
View File
@@ -30,6 +30,7 @@ _Widgets = {
"BECMainWindow": "BECMainWindow",
"BECProgressBar": "BECProgressBar",
"BECQueue": "BECQueue",
"BECQueueQt": "BECQueueQt",
"BECShell": "BECShell",
"BECStatusBox": "BECStatusBox",
"BeamlineStateManager": "BeamlineStateManager",
@@ -526,6 +527,30 @@ class BECQueue(RPCBase):
"""
class BECQueueQt(RPCBase):
"""The BEC queue with a QWidget view instead of QML, for comparing the two toolkits."""
_IMPORT_MODULE = "bec_widgets.widgets.services.bec_queue.bec_queue_qt"
@rpc_call
def remove(self):
"""
Cleanup the BECConnector
"""
@rpc_call
def attach(self):
"""
None
"""
@rpc_call
def detach(self):
"""
Detach the widget from its parent dock widget (if widget is in the dock), making it a floating widget.
"""
class BECShell(RPCBase):
"""A BecConsole pre-configured to run the BEC shell."""
+2
View File
@@ -16,6 +16,7 @@ designer_plugins = {
"BECProgressBar",
),
"BECQueue": ("bec_widgets.widgets.services.bec_queue.bec_queue", "BECQueue"),
"BECQueueQt": ("bec_widgets.widgets.services.bec_queue.bec_queue_qt", "BECQueueQt"),
"BECShell": ("bec_widgets.widgets.editors.bec_console.bec_console", "BECShell"),
"BECSpinBox": ("bec_widgets.widgets.utility.spinbox.decimal_spinbox", "BECSpinBox"),
"BECStatusBox": ("bec_widgets.widgets.services.bec_status_box.bec_status_box", "BECStatusBox"),
@@ -127,6 +128,7 @@ widget_icons = {
"BECMainWindow": "widgets",
"BECProgressBar": "page_control",
"BECQueue": "edit_note",
"BECQueueQt": "edit_note",
"BECShell": "hub",
"BECSpinBox": "123",
"BECStatusBox": "widgets",
@@ -376,7 +376,8 @@ class BECDockArea(DockAreaWidget):
),
}
util_actions = {
"queue": (widget_icons["BECQueue"], "Scan Queue", "BECQueue"),
"queue": (widget_icons["BECQueue"], "Scan Queue (QML)", "BECQueue"),
"queue_qt": (widget_icons["BECQueueQt"], "Scan Queue (QWidgets)", "BECQueueQt"),
"status": (widget_icons["BECStatusBox"], "BEC Status Box", "BECStatusBox"),
"progress_bar": (
widget_icons["RingProgressBar"],
@@ -2,7 +2,7 @@
QWidget rendering of the BEC queue, with the same UX as the QML view.
This exists to compare the two toolkits on the same data layer (:class:`QueueController`) and the
same shell (:class:`BECQueue`); only the view differs. It is not registered for RPC or Designer.
same shell (:class:`BECQueue`); only the view differs.
"""
from __future__ import annotations
@@ -732,8 +732,7 @@ class BECQueueQt(BECQueue):
Same shell, controller and behaviour as :class:`BECQueue`; only the rendering differs.
"""
PLUGIN = False
RPC = False
PLUGIN = True
def _create_view(self) -> QWidget:
return QueueView(self.controller, self)
@@ -0,0 +1 @@
{'files': ['bec_queue_qt.py']}
@@ -0,0 +1,57 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
from qtpy.QtWidgets import QWidget
from bec_widgets.utils.bec_designer import designer_material_icon
from bec_widgets.widgets.services.bec_queue.bec_queue_qt import BECQueueQt
DOM_XML = """
<ui language='c++'>
<widget class='BECQueueQt' name='bec_queue_qt'>
</widget>
</ui>
"""
class BECQueueQtPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
def __init__(self):
super().__init__()
self._form_editor = None
def createWidget(self, parent):
if parent is None:
return QWidget()
t = BECQueueQt(parent)
return t
def domXml(self):
return DOM_XML
def group(self):
return "BEC Services"
def icon(self):
return designer_material_icon(BECQueueQt.ICON_NAME)
def includeFile(self):
return "bec_queue_qt"
def initialize(self, form_editor):
self._form_editor = form_editor
def isContainer(self):
return False
def isInitialized(self):
return self._form_editor is not None
def name(self):
return "BECQueueQt"
def toolTip(self):
return "BEC queue with a QWidget view, for comparison with the QML view."
def whatsThis(self):
return self.toolTip()
@@ -0,0 +1,15 @@
def main(): # pragma: no cover
from qtpy import PYSIDE6
if not PYSIDE6:
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
return
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
from bec_widgets.widgets.services.bec_queue.bec_queue_qt_plugin import BECQueueQtPlugin
QPyDesignerCustomWidgetCollection.addCustomWidget(BECQueueQtPlugin())
if __name__ == "__main__": # pragma: no cover
main()
+18
View File
@@ -52,3 +52,21 @@ def test_qt_view_paused_banner_and_empty_state(qt_queue, busy_queue):
qt_queue.update_queue({}, {})
assert qt_queue.view._rows == {}
assert not qt_queue.view.empty.isHidden()
def test_dock_area_toolbar_adds_both_queue_views(qtbot, mocked_client):
"""The Dock Area toolbar offers the QML and the QWidget queue side by side."""
# pylint: disable=import-outside-toplevel
from bec_widgets.widgets.containers.dock_area.dock_area import BECDockArea
from bec_widgets.widgets.services.bec_queue.bec_queue import BECQueue
area = BECDockArea(client=mocked_client)
qtbot.addWidget(area)
qtbot.waitExposed(area)
menu = area.toolbar.components.get_action("menu_utils")
menu.actions["queue"].action.trigger()
menu.actions["queue_qt"].action.trigger()
widgets = list(area.widget_map().values())
assert sum(type(w) is BECQueue for w in widgets) == 1
assert sum(type(w) is BECQueueQt for w in widgets) == 1
+2
View File
@@ -887,6 +887,7 @@ class TestToolbarFunctionality:
"""Test utils toolbar actions trigger widget creation."""
utils_actions = [
"queue",
"queue_qt",
"terminal",
"status",
"progress_bar",
@@ -2496,6 +2497,7 @@ class TestFlatToolbarActions:
"""Test that flat utils actions are created."""
utils_actions = [
"flat_queue",
"flat_queue_qt",
"flat_status",
"flat_progress_bar",
"flat_scan_progress_bar",