mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-07-27 05:33:03 +02:00
fix(stop_button): enhance emergency stop functionality and update button behavior
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from bec_qthemes import material_icon
|
||||
from qtpy.QtCore import Qt
|
||||
from qtpy.QtCore import Qt, QTimer
|
||||
from qtpy.QtWidgets import QHBoxLayout, QPushButton, QSizePolicy, QToolButton, QWidget
|
||||
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
@@ -12,6 +12,9 @@ class StopButton(BECWidget, QWidget):
|
||||
PLUGIN = True
|
||||
ICON_NAME = "dangerous"
|
||||
RPC = False
|
||||
ABORT_LABEL = "Stop"
|
||||
EMERGENCY_STOP_LABEL = "Emergency Stop"
|
||||
EMERGENCY_STOP_TIMEOUT_MS = 2000
|
||||
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None, toolbar=False, **kwargs):
|
||||
super().__init__(parent=parent, client=client, gui_id=gui_id, config=config, **kwargs)
|
||||
@@ -22,15 +25,19 @@ class StopButton(BECWidget, QWidget):
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.layout.setAlignment(Qt.AlignmentFlag.AlignVCenter)
|
||||
self._emergency_stop_active = False
|
||||
self._reset_timer = QTimer(self)
|
||||
self._reset_timer.setSingleShot(True)
|
||||
self._reset_timer.timeout.connect(self._deactivate_emergency_stop)
|
||||
|
||||
if toolbar:
|
||||
icon = material_icon("stop", color="#cc181e", filled=True, convert_to_pixmap=False)
|
||||
self.button = QToolButton(icon=icon)
|
||||
self.button.setToolTip("Stop the scan queue")
|
||||
self.button.setToolTip(self.ABORT_LABEL)
|
||||
else:
|
||||
self.button = QPushButton()
|
||||
self.button.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
|
||||
self.button.setText("Stop")
|
||||
self.button.setText(self.ABORT_LABEL)
|
||||
self.button.setProperty("variant", "danger")
|
||||
self.button.clicked.connect(self.stop_scan)
|
||||
|
||||
@@ -41,12 +48,39 @@ class StopButton(BECWidget, QWidget):
|
||||
self,
|
||||
): # , scan_id: str | None = None): #FIXME scan_id will be added when combining with Queue widget
|
||||
"""
|
||||
Stop the scan.
|
||||
Abort the scan by default, then temporarily offer an emergency stop.
|
||||
|
||||
Args:
|
||||
scan_id(str|None): The scan id to stop. If None, the current scan will be stopped.
|
||||
scan_id(str|None): The scan id to stop. If None, the current scan will be targeted.
|
||||
"""
|
||||
self.queue.request_scan_halt()
|
||||
if self._emergency_stop_active:
|
||||
self.queue.request_scan_halt()
|
||||
self._activate_emergency_stop()
|
||||
return
|
||||
|
||||
self.queue.request_scan_abortion()
|
||||
self._activate_emergency_stop()
|
||||
|
||||
def _activate_emergency_stop(self) -> None:
|
||||
self._emergency_stop_active = True
|
||||
self._set_button_label(self.EMERGENCY_STOP_LABEL)
|
||||
self._reset_timer.start(self.EMERGENCY_STOP_TIMEOUT_MS)
|
||||
|
||||
def _deactivate_emergency_stop(self) -> None:
|
||||
self._emergency_stop_active = False
|
||||
self._set_button_label(self.ABORT_LABEL)
|
||||
|
||||
def _set_button_label(self, label: str) -> None:
|
||||
if hasattr(self.button, "setText"):
|
||||
self.button.setText(label)
|
||||
self.button.setToolTip(label)
|
||||
|
||||
def cleanup(self):
|
||||
"""Stop and dispose the emergency-stop reset timer before widget teardown."""
|
||||
self._reset_timer.stop()
|
||||
self._reset_timer.timeout.disconnect(self._deactivate_emergency_stop)
|
||||
self._reset_timer.deleteLater()
|
||||
super().cleanup()
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# pylint: disable=missing-function-docstring, missing-module-docstring, unused-import
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from bec_widgets.widgets.control.buttons.stop_button.stop_button import StopButton
|
||||
@@ -10,13 +12,48 @@ from .client_mocks import mocked_client
|
||||
@pytest.fixture
|
||||
def stop_button(qtbot, mocked_client):
|
||||
widget = StopButton(client=mocked_client)
|
||||
widget.queue = MagicMock()
|
||||
qtbot.addWidget(widget)
|
||||
qtbot.waitExposed(widget)
|
||||
yield widget
|
||||
yield widget, qtbot
|
||||
|
||||
|
||||
def test_stop_button(stop_button):
|
||||
assert stop_button.button.text() == "Stop"
|
||||
stop_button.button.click()
|
||||
assert stop_button.queue.request_scan_halt.called
|
||||
stop_button.close()
|
||||
widget, qtbot = stop_button
|
||||
|
||||
assert widget.button.text() == "Stop"
|
||||
|
||||
widget.button.click()
|
||||
|
||||
assert widget.queue.request_scan_abortion.called
|
||||
assert widget.button.text() == "Emergency Stop"
|
||||
|
||||
qtbot.wait(widget.EMERGENCY_STOP_TIMEOUT_MS // 2)
|
||||
widget.button.click()
|
||||
|
||||
assert widget.queue.request_scan_halt.called
|
||||
|
||||
qtbot.wait((widget.EMERGENCY_STOP_TIMEOUT_MS // 2) + 100)
|
||||
assert widget.button.text() == "Emergency Stop"
|
||||
|
||||
qtbot.wait((widget.EMERGENCY_STOP_TIMEOUT_MS // 2) + 100)
|
||||
assert widget.button.text() == "Stop"
|
||||
|
||||
widget.close()
|
||||
|
||||
|
||||
def test_stop_button_click_extends_emergency_timeout(stop_button):
|
||||
widget, qtbot = stop_button
|
||||
|
||||
widget.button.click()
|
||||
qtbot.wait(widget.EMERGENCY_STOP_TIMEOUT_MS - 250)
|
||||
|
||||
widget.button.click()
|
||||
assert widget.button.text() == "Emergency Stop"
|
||||
|
||||
qtbot.wait(200)
|
||||
assert widget.button.text() == "Emergency Stop"
|
||||
|
||||
qtbot.wait(widget.EMERGENCY_STOP_TIMEOUT_MS - 100)
|
||||
assert widget.button.text() == "Stop"
|
||||
widget.close()
|
||||
|
||||
Reference in New Issue
Block a user