mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-06-18 19:11:14 +02:00
fix(notification-center): sync light theme styling
This commit is contained in:
+31
-18
@@ -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(
|
||||
|
||||
@@ -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
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user