diff --git a/bec_widgets/cli/client.py b/bec_widgets/cli/client.py index 56b0f853..115cd991 100644 --- a/bec_widgets/cli/client.py +++ b/bec_widgets/cli/client.py @@ -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.""" diff --git a/bec_widgets/cli/designer_plugins.py b/bec_widgets/cli/designer_plugins.py index 9c51d995..11933b23 100644 --- a/bec_widgets/cli/designer_plugins.py +++ b/bec_widgets/cli/designer_plugins.py @@ -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", diff --git a/bec_widgets/widgets/containers/dock_area/dock_area.py b/bec_widgets/widgets/containers/dock_area/dock_area.py index 8d7e5f54..de641d21 100644 --- a/bec_widgets/widgets/containers/dock_area/dock_area.py +++ b/bec_widgets/widgets/containers/dock_area/dock_area.py @@ -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"], diff --git a/bec_widgets/widgets/services/bec_queue/bec_queue_qt.py b/bec_widgets/widgets/services/bec_queue/bec_queue_qt.py index 26d2eea1..46812fb7 100644 --- a/bec_widgets/widgets/services/bec_queue/bec_queue_qt.py +++ b/bec_widgets/widgets/services/bec_queue/bec_queue_qt.py @@ -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) diff --git a/bec_widgets/widgets/services/bec_queue/bec_queue_qt.pyproject b/bec_widgets/widgets/services/bec_queue/bec_queue_qt.pyproject new file mode 100644 index 00000000..f3f38456 --- /dev/null +++ b/bec_widgets/widgets/services/bec_queue/bec_queue_qt.pyproject @@ -0,0 +1 @@ +{'files': ['bec_queue_qt.py']} \ No newline at end of file diff --git a/bec_widgets/widgets/services/bec_queue/bec_queue_qt_plugin.py b/bec_widgets/widgets/services/bec_queue/bec_queue_qt_plugin.py new file mode 100644 index 00000000..ae646b21 --- /dev/null +++ b/bec_widgets/widgets/services/bec_queue/bec_queue_qt_plugin.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 = """ + + + + +""" + + +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() diff --git a/bec_widgets/widgets/services/bec_queue/register_bec_queue_qt.py b/bec_widgets/widgets/services/bec_queue/register_bec_queue_qt.py new file mode 100644 index 00000000..d83f4e1f --- /dev/null +++ b/bec_widgets/widgets/services/bec_queue/register_bec_queue_qt.py @@ -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() diff --git a/tests/unit_tests/test_bec_queue_qt.py b/tests/unit_tests/test_bec_queue_qt.py index b1424a60..1de85702 100644 --- a/tests/unit_tests/test_bec_queue_qt.py +++ b/tests/unit_tests/test_bec_queue_qt.py @@ -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 diff --git a/tests/unit_tests/test_dock_area.py b/tests/unit_tests/test_dock_area.py index fa91bbc1..14cfe27d 100644 --- a/tests/unit_tests/test_dock_area.py +++ b/tests/unit_tests/test_dock_area.py @@ -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",