1
0
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:
2026-01-16 16:38:12 +01:00
committed by Christian Appel
parent b38d6dc549
commit b1a3403cd3
5 changed files with 93 additions and 65 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()

View File

@@ -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())

View File

@@ -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."""