From a568633c3206a8c26069d140f2d9a548bf4124b0 Mon Sep 17 00:00:00 2001 From: wyzula-jan Date: Thu, 29 Aug 2024 14:07:52 +0200 Subject: [PATCH] fix(abort_button): abort button added; some minor fixes --- bec_widgets/cli/client.py | 23 +++++++- bec_widgets/widgets/button_abort/__init__.py | 0 .../button_abort/abort_button.pyproject | 1 + .../button_abort/abort_button_plugin.py | 54 +++++++++++++++++++ .../widgets/button_abort/button_abort.py | 49 +++++++++++++++++ .../button_abort/register_abort_button.py | 15 ++++++ .../widgets/button_resume/button_resume.py | 2 +- .../widgets/positioner_box/positioner_box.py | 2 + .../widgets/stop_button/stop_button.py | 11 +++- tests/unit_tests/test_abort_button.py | 26 +++++++++ tests/unit_tests/test_resume_button.py | 2 +- 11 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 bec_widgets/widgets/button_abort/__init__.py create mode 100644 bec_widgets/widgets/button_abort/abort_button.pyproject create mode 100644 bec_widgets/widgets/button_abort/abort_button_plugin.py create mode 100644 bec_widgets/widgets/button_abort/button_abort.py create mode 100644 bec_widgets/widgets/button_abort/register_abort_button.py create mode 100644 tests/unit_tests/test_abort_button.py diff --git a/bec_widgets/cli/client.py b/bec_widgets/cli/client.py index 128a595e..3306e0ad 100644 --- a/bec_widgets/cli/client.py +++ b/bec_widgets/cli/client.py @@ -13,6 +13,7 @@ class Widgets(str, enum.Enum): Enum for the available widgets. """ + AbortButton = "AbortButton" BECDock = "BECDock" BECDockArea = "BECDockArea" BECFigure = "BECFigure" @@ -36,6 +37,24 @@ class Widgets(str, enum.Enum): WebsiteWidget = "WebsiteWidget" +class AbortButton(RPCBase): + @property + @rpc_call + def _config_dict(self) -> "dict": + """ + Get the configuration of the widget. + + Returns: + dict: The configuration of the widget. + """ + + @rpc_call + def _get_all_rpc(self) -> "dict": + """ + Get all registered RPC objects. + """ + + class BECCurve(RPCBase): @rpc_call def remove(self): @@ -2374,7 +2393,7 @@ class DeviceLineEdit(RPCBase): class PositionerBox(RPCBase): @rpc_call - def set_positioner(self, positioner: str): + def set_positioner(self, positioner: "str | Positioner"): """ Set the device @@ -2385,7 +2404,7 @@ class PositionerBox(RPCBase): class PositionerControlLine(RPCBase): @rpc_call - def set_positioner(self, positioner: str): + def set_positioner(self, positioner: "str | Positioner"): """ Set the device diff --git a/bec_widgets/widgets/button_abort/__init__.py b/bec_widgets/widgets/button_abort/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bec_widgets/widgets/button_abort/abort_button.pyproject b/bec_widgets/widgets/button_abort/abort_button.pyproject new file mode 100644 index 00000000..2e8c266c --- /dev/null +++ b/bec_widgets/widgets/button_abort/abort_button.pyproject @@ -0,0 +1 @@ +{'files': ['button_abort.py']} \ No newline at end of file diff --git a/bec_widgets/widgets/button_abort/abort_button_plugin.py b/bec_widgets/widgets/button_abort/abort_button_plugin.py new file mode 100644 index 00000000..2efe02c9 --- /dev/null +++ b/bec_widgets/widgets/button_abort/abort_button_plugin.py @@ -0,0 +1,54 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from qtpy.QtDesigner import QDesignerCustomWidgetInterface + +from bec_widgets.utils.bec_designer import designer_material_icon +from bec_widgets.widgets.button_abort.button_abort import AbortButton + +DOM_XML = """ + + + + +""" + + +class AbortButtonPlugin(QDesignerCustomWidgetInterface): # pragma: no cover + def __init__(self): + super().__init__() + self._form_editor = None + + def createWidget(self, parent): + t = AbortButton(parent) + return t + + def domXml(self): + return DOM_XML + + def group(self): + return "BEC Buttons" + + def icon(self): + return designer_material_icon(AbortButton.ICON_NAME) + + def includeFile(self): + return "abort_button" + + 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 "AbortButton" + + def toolTip(self): + return "A button that abort the scan." + + def whatsThis(self): + return self.toolTip() diff --git a/bec_widgets/widgets/button_abort/button_abort.py b/bec_widgets/widgets/button_abort/button_abort.py new file mode 100644 index 00000000..961d1e8d --- /dev/null +++ b/bec_widgets/widgets/button_abort/button_abort.py @@ -0,0 +1,49 @@ +from bec_qthemes import material_icon +from qtpy.QtCore import Qt +from qtpy.QtWidgets import QHBoxLayout, QPushButton, QToolButton, QWidget + +from bec_widgets.qt_utils.error_popups import SafeSlot +from bec_widgets.utils.bec_widget import BECWidget + + +class AbortButton(BECWidget, QWidget): + """A button that abort the scan.""" + + ICON_NAME = "cancel" + + def __init__(self, parent=None, client=None, config=None, gui_id=None, toolbar=False): + super().__init__(client=client, config=config, gui_id=gui_id) + QWidget.__init__(self, parent=parent) + + self.get_bec_shortcuts() + + self.layout = QHBoxLayout(self) + self.layout.setSpacing(0) + self.layout.setContentsMargins(0, 0, 0, 0) + self.layout.setAlignment(Qt.AlignmentFlag.AlignVCenter) + + if toolbar: + icon = material_icon("cancel", color="#666666", filled=True) + self.button = QToolButton(icon=icon) + self.button.triggered.connect(self.abort_scan) + else: + self.button = QPushButton() + self.button.setText("Abort") + self.button.setStyleSheet( + "background-color: #666666; color: white; font-weight: bold; font-size: 12px;" + ) + self.button.clicked.connect(self.abort_scan) + + self.layout.addWidget(self.button) + + @SafeSlot() + def abort_scan( + self, + ): # , scan_id: str | None = None): #FIXME scan_id will be added when combining with Queue widget + """ + Abort the scan. + + Args: + scan_id(str|None): The scan id to abort. If None, the current scan will be aborted. + """ + self.queue.request_scan_abortion() diff --git a/bec_widgets/widgets/button_abort/register_abort_button.py b/bec_widgets/widgets/button_abort/register_abort_button.py new file mode 100644 index 00000000..d2205089 --- /dev/null +++ b/bec_widgets/widgets/button_abort/register_abort_button.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.button_abort.abort_button_plugin import AbortButtonPlugin + + QPyDesignerCustomWidgetCollection.addCustomWidget(AbortButtonPlugin()) + + +if __name__ == "__main__": # pragma: no cover + main() diff --git a/bec_widgets/widgets/button_resume/button_resume.py b/bec_widgets/widgets/button_resume/button_resume.py index 15c0db3b..6f28e1d5 100644 --- a/bec_widgets/widgets/button_resume/button_resume.py +++ b/bec_widgets/widgets/button_resume/button_resume.py @@ -30,7 +30,7 @@ class ResumeButton(BECWidget, QWidget): self.button = QPushButton() self.button.setText("Resume") self.button.setStyleSheet( - "background-color: #2793e8 color: white; font-weight: bold; font-size: 12px;" + "background-color: #2793e8; color: white; font-weight: bold; font-size: 12px;" ) self.button.clicked.connect(self.continue_scan) diff --git a/bec_widgets/widgets/positioner_box/positioner_box.py b/bec_widgets/widgets/positioner_box/positioner_box.py index e51bedf8..5f3bbe53 100644 --- a/bec_widgets/widgets/positioner_box/positioner_box.py +++ b/bec_widgets/widgets/positioner_box/positioner_box.py @@ -1,5 +1,7 @@ """ Module for a PositionerBox widget to control a positioner device.""" +from __future__ import annotations + import os import uuid diff --git a/bec_widgets/widgets/stop_button/stop_button.py b/bec_widgets/widgets/stop_button/stop_button.py index 2391790d..41e7c504 100644 --- a/bec_widgets/widgets/stop_button/stop_button.py +++ b/bec_widgets/widgets/stop_button/stop_button.py @@ -37,6 +37,13 @@ class StopButton(BECWidget, QWidget): self.layout.addWidget(self.button) @SafeSlot() - def stop_scan(self): - """Stop the scan.""" + def stop_scan( + self, + ): # , scan_id: str | None = None): #FIXME scan_id will be added when combining with Queue widget + """ + Stop the scan. + + Args: + scan_id(str|None): The scan id to stop. If None, the current scan will be stopped. + """ self.queue.request_scan_halt() diff --git a/tests/unit_tests/test_abort_button.py b/tests/unit_tests/test_abort_button.py new file mode 100644 index 00000000..622151a6 --- /dev/null +++ b/tests/unit_tests/test_abort_button.py @@ -0,0 +1,26 @@ +# pylint: disable=missing-function-docstring, missing-module-docstring, unused-import + +import pytest + +from bec_widgets.widgets.button_abort.button_abort import AbortButton + +from .client_mocks import mocked_client + + +@pytest.fixture +def abort_button(qtbot, mocked_client): + widget = AbortButton(client=mocked_client) + qtbot.addWidget(widget) + qtbot.waitExposed(widget) + yield widget + + +def test_abort_button(abort_button): + assert abort_button.button.text() == "Abort" + assert ( + abort_button.button.styleSheet() + == "background-color: #666666; color: white; font-weight: bold; font-size: 12px;" + ) + abort_button.button.click() + assert abort_button.queue.request_scan_abortion.called + abort_button.close() diff --git a/tests/unit_tests/test_resume_button.py b/tests/unit_tests/test_resume_button.py index 750c816c..534f4986 100644 --- a/tests/unit_tests/test_resume_button.py +++ b/tests/unit_tests/test_resume_button.py @@ -19,7 +19,7 @@ def test_resume_button(resume_button): assert resume_button.button.text() == "Resume" assert ( resume_button.button.styleSheet() - == "background-color: #2793e8 color: white; font-weight: bold; font-size: 12px;" + == "background-color: #2793e8; color: white; font-weight: bold; font-size: 12px;" ) resume_button.button.click() assert resume_button.queue.request_scan_continuation.called