mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-03-05 00:12:49 +01:00
fix(busy-loader): adjust busy loader and tests
This commit is contained in:
@@ -248,6 +248,7 @@ class DeviceManagerDisplayWidget(DockAreaWidget):
|
||||
|
||||
def _set_busy_wrapper(self, enabled: bool):
|
||||
"""Thin wrapper around set_busy to flip the state variable."""
|
||||
self._busy_overlay.set_opacity(0.8)
|
||||
self._config_upload_active = enabled
|
||||
self.set_busy(enabled=enabled)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from qtpy.QtWidgets import QApplication, QFileDialog, QLabel, QVBoxLayout, QWidg
|
||||
import bec_widgets.widgets.containers.qt_ads as QtAds
|
||||
from bec_widgets.cli.rpc.rpc_register import RPCRegister
|
||||
from bec_widgets.utils.bec_connector import BECConnector, ConnectionConfig
|
||||
from bec_widgets.utils.busy_loader import install_busy_loader
|
||||
from bec_widgets.utils.error_popups import SafeConnect, SafeSlot
|
||||
from bec_widgets.utils.rpc_decorator import rpc_timeout
|
||||
from bec_widgets.utils.widget_io import WidgetHierarchy
|
||||
@@ -70,9 +71,9 @@ class BECWidget(BECConnector):
|
||||
self._busy_state_widget: QWidget | None = None
|
||||
|
||||
self._loading = False
|
||||
self._busy_overlay = self._install_busy_loader()
|
||||
if start_busy and isinstance(self, QWidget):
|
||||
self._busy_overlay = self._install_busy_loader()
|
||||
self._adjust_busy_overlay()
|
||||
self._show_busy_overlay()
|
||||
self._loading = True
|
||||
|
||||
def _connect_to_theme_change(self):
|
||||
@@ -152,7 +153,6 @@ class BECWidget(BECConnector):
|
||||
child.stop()
|
||||
|
||||
widget = BusyStateWidget(self)
|
||||
|
||||
return widget
|
||||
|
||||
def _install_busy_loader(self) -> "BusyLoaderOverlay" | None:
|
||||
@@ -164,7 +164,6 @@ class BECWidget(BECConnector):
|
||||
return None
|
||||
overlay = getattr(self, "_busy_overlay", None)
|
||||
if overlay is None:
|
||||
from bec_widgets.utils.busy_loader import install_busy_loader
|
||||
|
||||
overlay = install_busy_loader(self, start_loading=False)
|
||||
self._busy_overlay = overlay
|
||||
@@ -174,7 +173,7 @@ class BECWidget(BECConnector):
|
||||
self._busy_overlay.set_widget(self._busy_state_widget)
|
||||
return overlay
|
||||
|
||||
def _adjust_busy_overlay(self) -> None:
|
||||
def _show_busy_overlay(self) -> None:
|
||||
"""Create and attach the loading overlay to this widget if QWidget is present."""
|
||||
if not isinstance(self, QWidget):
|
||||
return
|
||||
@@ -198,7 +197,7 @@ class BECWidget(BECConnector):
|
||||
if self._busy_overlay is None:
|
||||
self._busy_overlay = self._install_busy_loader()
|
||||
if enabled:
|
||||
self._adjust_busy_overlay()
|
||||
self._show_busy_overlay()
|
||||
else:
|
||||
self._busy_overlay.hide()
|
||||
self._loading = bool(enabled)
|
||||
|
||||
@@ -13,10 +13,8 @@ from qtpy.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from bec_widgets import BECWidget
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
from bec_widgets.utils.error_popups import SafeProperty
|
||||
from bec_widgets.widgets.plots.waveform.waveform import Waveform
|
||||
|
||||
|
||||
class _OverlayEventFilter(QObject):
|
||||
@@ -56,14 +54,14 @@ class BusyLoaderOverlay(QWidget):
|
||||
foreground_color_changed = Signal(QColor)
|
||||
scrim_color_changed = Signal(QColor)
|
||||
|
||||
def __init__(self, parent: QWidget, opacity: float = 0.85, **kwargs):
|
||||
def __init__(self, parent: QWidget, opacity: float = 0.35, **kwargs):
|
||||
super().__init__(parent=parent, **kwargs)
|
||||
|
||||
self.setAttribute(Qt.WA_StyledBackground, True)
|
||||
self.setAutoFillBackground(False)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground, True)
|
||||
self._opacity = opacity
|
||||
self._scrim_color = QColor(0, 0, 0, 110)
|
||||
self._scrim_color = QColor(128, 128, 128, 110)
|
||||
self._label_color = QColor(240, 240, 240)
|
||||
self._filter: QObject | None = None
|
||||
|
||||
@@ -165,7 +163,7 @@ class BusyLoaderOverlay(QWidget):
|
||||
base = self.scrim_color
|
||||
base.setAlpha(int(255 * self._opacity))
|
||||
self.scrim_color = base
|
||||
self.update()
|
||||
self._update_palette()
|
||||
|
||||
##########################
|
||||
### Internal methods ###
|
||||
@@ -193,8 +191,9 @@ class BusyLoaderOverlay(QWidget):
|
||||
self.foreground_color = fg
|
||||
|
||||
# Set the frame style with updated foreground colors
|
||||
r, g, b, a = base.getRgb()
|
||||
self._frame.setStyleSheet(
|
||||
f"#busyFrame {{ border: 2px dashed {self.foreground_color.name()}; border-radius: 9px; background-color: rgba(128, 128, 128, 110); }}"
|
||||
f"#busyFrame {{ border: 2px dashed {self.foreground_color.name()}; border-radius: 9px; background-color: rgba({r}, {g}, {b}, {a}); }}"
|
||||
)
|
||||
self.update()
|
||||
|
||||
@@ -255,62 +254,63 @@ def install_busy_loader(
|
||||
# --------------------------
|
||||
# Launchable demo
|
||||
# --------------------------
|
||||
class DemoWidget(BECWidget, QWidget): # pragma: no cover
|
||||
def __init__(self, parent=None, start_busy: bool = False):
|
||||
super().__init__(parent=parent, theme_update=True, start_busy=start_busy)
|
||||
|
||||
self._title = QLabel("Demo Content", self)
|
||||
self._title.setAlignment(Qt.AlignCenter)
|
||||
self._title.setFrameStyle(QFrame.Panel | QFrame.Sunken)
|
||||
lay = QVBoxLayout(self)
|
||||
lay.addWidget(self._title)
|
||||
waveform = Waveform(self)
|
||||
waveform.plot([1, 2, 3, 4, 5])
|
||||
lay.addWidget(waveform, 1)
|
||||
|
||||
QTimer.singleShot(5000, self._ready)
|
||||
|
||||
def _ready(self):
|
||||
self._title.setText("Ready ✓")
|
||||
self.set_busy(False)
|
||||
|
||||
|
||||
class DemoWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Busy Loader — BECWidget demo")
|
||||
|
||||
left = DemoWidget(start_busy=True)
|
||||
right = DemoWidget()
|
||||
|
||||
btn_on = QPushButton("Right → Loading")
|
||||
btn_off = QPushButton("Right → Ready")
|
||||
btn_text = QPushButton("Set custom text")
|
||||
btn_on.clicked.connect(lambda: right.set_busy(True))
|
||||
btn_off.clicked.connect(lambda: right.set_busy(False))
|
||||
|
||||
panel = QWidget()
|
||||
prow = QVBoxLayout(panel)
|
||||
prow.addWidget(btn_on)
|
||||
prow.addWidget(btn_off)
|
||||
prow.addWidget(btn_text)
|
||||
prow.addStretch(1)
|
||||
|
||||
central = QWidget()
|
||||
row = QHBoxLayout(central)
|
||||
row.setContentsMargins(12, 12, 12, 12)
|
||||
row.setSpacing(12)
|
||||
row.addWidget(left, 1)
|
||||
row.addWidget(right, 1)
|
||||
row.addWidget(panel, 0)
|
||||
|
||||
self.setCentralWidget(central)
|
||||
self.resize(900, 420)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
import sys
|
||||
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.widgets.plots.waveform.waveform import Waveform
|
||||
|
||||
class DemoWidget(BECWidget, QWidget): # pragma: no cover
|
||||
def __init__(self, parent=None, start_busy: bool = False):
|
||||
super().__init__(parent=parent, theme_update=True, start_busy=start_busy)
|
||||
|
||||
self._title = QLabel("Demo Content", self)
|
||||
self._title.setAlignment(Qt.AlignCenter)
|
||||
self._title.setFrameStyle(QFrame.Panel | QFrame.Sunken)
|
||||
lay = QVBoxLayout(self)
|
||||
lay.addWidget(self._title)
|
||||
waveform = Waveform(self)
|
||||
waveform.plot([1, 2, 3, 4, 5])
|
||||
lay.addWidget(waveform, 1)
|
||||
|
||||
QTimer.singleShot(5000, self._ready)
|
||||
|
||||
def _ready(self):
|
||||
self._title.setText("Ready ✓")
|
||||
self.set_busy(False)
|
||||
|
||||
class DemoWindow(QMainWindow): # pragma: no cover
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Busy Loader — BECWidget demo")
|
||||
|
||||
left = DemoWidget(start_busy=True)
|
||||
right = DemoWidget()
|
||||
|
||||
btn_on = QPushButton("Right → Loading")
|
||||
btn_off = QPushButton("Right → Ready")
|
||||
btn_text = QPushButton("Set custom text")
|
||||
btn_on.clicked.connect(lambda: right.set_busy(True))
|
||||
btn_off.clicked.connect(lambda: right.set_busy(False))
|
||||
|
||||
panel = QWidget()
|
||||
prow = QVBoxLayout(panel)
|
||||
prow.addWidget(btn_on)
|
||||
prow.addWidget(btn_off)
|
||||
prow.addWidget(btn_text)
|
||||
prow.addStretch(1)
|
||||
|
||||
central = QWidget()
|
||||
row = QHBoxLayout(central)
|
||||
row.setContentsMargins(12, 12, 12, 12)
|
||||
row.setSpacing(12)
|
||||
row.addWidget(left, 1)
|
||||
row.addWidget(right, 1)
|
||||
row.addWidget(panel, 0)
|
||||
|
||||
self.setCentralWidget(central)
|
||||
self.resize(900, 420)
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
apply_theme("light")
|
||||
w = DemoWindow()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
from qtpy.QtWidgets import QLabel, QVBoxLayout, QWidget
|
||||
|
||||
@@ -68,6 +69,24 @@ def test_becwidget_set_busy_toggle_and_text(qtbot, widget_idle):
|
||||
qtbot.waitUntil(lambda: overlay.isHidden())
|
||||
|
||||
|
||||
def test_becwidget_busy_overlay_set_opacity(qtbot, widget_busy):
|
||||
overlay = getattr(widget_busy, "_busy_overlay")
|
||||
qtbot.waitUntil(lambda: overlay.isVisible())
|
||||
|
||||
# Default opacity is 0.7
|
||||
frame = getattr(overlay, "_frame", None)
|
||||
assert frame is not None
|
||||
sheet = frame.styleSheet()
|
||||
_, _, _, a = overlay.scrim_color.getRgb()
|
||||
assert np.isclose(a / 255, 0.35, atol=0.02)
|
||||
|
||||
# Change opacity
|
||||
overlay.set_opacity(0.7)
|
||||
qtbot.waitUntil(lambda: overlay.isVisible())
|
||||
_, _, _, a = overlay.scrim_color.getRgb()
|
||||
assert np.isclose(a / 255, 0.7, atol=0.02)
|
||||
|
||||
|
||||
def test_becwidget_overlay_tracks_resize(qtbot, widget_busy):
|
||||
overlay = getattr(widget_busy, "_busy_overlay")
|
||||
qtbot.waitUntil(lambda: overlay.geometry() == widget_busy.rect())
|
||||
|
||||
@@ -23,6 +23,7 @@ from bec_widgets.applications.views.device_manager_view.device_manager_dialogs.u
|
||||
ValidationSection,
|
||||
)
|
||||
from bec_widgets.applications.views.device_manager_view.device_manager_display_widget import (
|
||||
CustomBusyWidget,
|
||||
DeviceManagerDisplayWidget,
|
||||
)
|
||||
from bec_widgets.applications.views.device_manager_view.device_manager_view import (
|
||||
@@ -592,6 +593,14 @@ class TestDeviceManagerView:
|
||||
qtbot.waitExposed(widget)
|
||||
yield widget
|
||||
|
||||
@pytest.fixture
|
||||
def custom_busy(self, qtbot, mocked_client):
|
||||
"""Fixture for the custom busy widget of the DeviceManagerDisplayWidget."""
|
||||
widget = CustomBusyWidget(client=mocked_client)
|
||||
qtbot.addWidget(widget)
|
||||
qtbot.waitExposed(widget)
|
||||
yield widget
|
||||
|
||||
@pytest.fixture
|
||||
def device_configs(self, device_config: dict):
|
||||
"""Fixture for multiple device configurations."""
|
||||
|
||||
Reference in New Issue
Block a user