From f78bc26a26bfebfc7df83a9c6cd7dd5c95a35d05 Mon Sep 17 00:00:00 2001 From: wyzula-jan Date: Tue, 9 Jun 2026 10:59:36 +0200 Subject: [PATCH] fix(notification-center): sync light theme styling --- .../notification_banner.py | 49 ++++++++++++------- tests/unit_tests/test_notifications.py | 26 ++++++++++ 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/bec_widgets/widgets/containers/main_window/addons/notification_center/notification_banner.py b/bec_widgets/widgets/containers/main_window/addons/notification_center/notification_banner.py index 53f7a204..304a4c9d 100644 --- a/bec_widgets/widgets/containers/main_window/addons/notification_center/notification_banner.py +++ b/bec_widgets/widgets/containers/main_window/addons/notification_center/notification_banner.py @@ -11,7 +11,6 @@ Intended for use in desktop applications to provide user feedback, warnings, and from __future__ import annotations -import json import sys from datetime import datetime from enum import Enum @@ -21,6 +20,7 @@ from uuid import uuid4 import pyqtgraph as pg from bec_lib.alarm_handler import Alarms # external enum from bec_lib.endpoints import MessageEndpoints +from bec_lib.logger import bec_logger from bec_lib.messages import ErrorInfo from bec_qthemes import material_icon from qtpy import QtCore, QtGui, QtWidgets @@ -29,9 +29,11 @@ from qtpy.QtWidgets import QApplication, QFrame, QMainWindow, QScrollArea, QWidg from bec_widgets import SafeProperty, SafeSlot from bec_widgets.utils.bec_connector import BECConnector -from bec_widgets.utils.colors import apply_theme +from bec_widgets.utils.colors import apply_theme, get_theme_name from bec_widgets.utils.widget_io import WidgetIO +logger = bec_logger.logger + class SeverityKind(str, Enum): INFO = "info" @@ -258,8 +260,10 @@ class NotificationToast(QFrame): def _connect_to_theme_change(self): """Connect this toast to the global theme‑updated signal.""" qapp = QApplication.instance() - if hasattr(qapp, "theme_signal"): - qapp.theme_signal.theme_updated.connect(self.apply_theme) + if hasattr(qapp, "theme"): + qapp.theme.theme_changed.connect(self.apply_theme) + else: + logger.warning("Theme could not be fetched form QApplication object.") # helper methods ----------------------------------------------------- def _current_inner_width(self) -> int: @@ -354,11 +358,9 @@ class NotificationToast(QFrame): Args: theme(str | None): "light" or "dark". If None, auto-detects from QApplication. """ - # determine effective theme - if theme is None: - app = QApplication.instance() - theme = getattr(getattr(app, "theme", None), "theme", "dark") - theme = theme.lower() + theme = str(theme or get_theme_name()).lower() + if theme not in {"light", "dark"}: + theme = "dark" self._theme = theme palette = DARK_PALETTE if theme == "dark" else LIGHT_PALETTE @@ -403,11 +405,18 @@ class NotificationToast(QFrame): #NotificationToast QPushButton:hover {{ color: {btn_hover}; }} """) # traceback panel colours - trace_bg = "#1e1e1e" if theme == "dark" else "#f0f0f0" + if theme == "dark": + trace_bg = "#1e1e1e" + trace_fg = palette["body"] + trace_border = "rgba(255,255,255,48)" + else: + trace_bg = "#ffffff" + trace_fg = palette["body"] + trace_border = "rgba(15,23,42,54)" self.trace_view.setStyleSheet(f""" background:{trace_bg}; - color:{palette['body']}; - border:none; + color:{trace_fg}; + border: 1px solid {trace_border}; border-radius:8px; """) @@ -438,8 +447,8 @@ class NotificationToast(QFrame): }} """) - # stronger accent wash in light mode, slightly stronger in dark too - self._accent_alpha = 110 if theme == "light" else 60 + self._accent_alpha = 6 if theme == "light" else 60 + self._gradient_width_factor = 1.0 if theme == "light" else 0.70 self.update() ######################################## @@ -519,7 +528,9 @@ class NotificationToast(QFrame): painter.fillPath(path, self._base_color) # accent gradient, fades to transparent - grad = QtGui.QLinearGradient(0, 0, self.width() * 0.7, 0) + grad = QtGui.QLinearGradient( + 0, 0, self.width() * getattr(self, "_gradient_width_factor", 0.70), 0 + ) accent = QtGui.QColor(self._accent_color) if getattr(self, "_theme", "dark") == "light": accent = accent.darker(115) @@ -543,7 +554,7 @@ class NotificationToast(QFrame): def close(self) -> None: self.closed.emit() - QtWidgets.QApplication.instance().removeEventFilter(self) + self.time_lbl.removeEventFilter(self) super().close() # this will remove the widget from its parent @@ -673,8 +684,10 @@ class NotificationCentre(QScrollArea): def _connect_to_theme_change(self): """Connect to the theme change signal.""" qapp = QApplication.instance() - if hasattr(qapp, "theme_signal"): - qapp.theme_signal.theme_updated.connect(self.apply_theme) + if hasattr(qapp, "theme"): + qapp.theme.theme_changed.connect(self.apply_theme) + else: + logger.warning("Theme could not be fetched form QApplication object.") # public API def add_notification( diff --git a/tests/unit_tests/test_notifications.py b/tests/unit_tests/test_notifications.py index 3ef5f5a3..67eec14a 100644 --- a/tests/unit_tests/test_notifications.py +++ b/tests/unit_tests/test_notifications.py @@ -40,11 +40,23 @@ def test_apply_theme_updates_colours(qtbot, toast): """apply_theme("light") should inject LIGHT palette colours into stylesheets.""" toast.apply_theme("light") assert LIGHT_PALETTE["title"] in toast._title_lbl.styleSheet() + assert "border: 1px solid" in toast.trace_view.styleSheet() + assert "border:none" not in toast.trace_view.styleSheet() toast.apply_theme("dark") assert DARK_PALETTE["title"] in toast._title_lbl.styleSheet() +def test_toast_updates_from_qapp_theme_changed_signal(qtbot, toast): + app = QtWidgets.QApplication.instance() + assert hasattr(app, "theme") + + app.theme.theme_changed.emit("light") + qtbot.wait(10) + + assert LIGHT_PALETTE["title"] in toast._title_lbl.styleSheet() + + def test_expired_signal(qtbot, toast): """Toast must emit expired once its lifetime finishes.""" with qtbot.waitSignal(toast.expired, timeout=1000): @@ -251,6 +263,20 @@ def test_theme_propagation(qtbot, centre): assert LIGHT_PALETTE["title"] in toast._title_lbl.styleSheet() +def test_centre_updates_from_qapp_theme_changed_signal(qtbot, centre): + toast = _post(centre, SeverityKind.INFO) + centre.apply_theme("dark") + + app = QtWidgets.QApplication.instance() + assert hasattr(app, "theme") + + app.theme.theme_changed.emit("light") + qtbot.wait(10) + + assert centre._theme == "light" + assert LIGHT_PALETTE["title"] in toast._title_lbl.styleSheet() + + # ------------------------------------------------------------------------ # NotificationIndicator tests # ------------------------------------------------------------------------