feat: theme-aware beamline state panel
The panel paints availability/message colors in code per DAQ tick, so QSS can't retint it — state_colors(theme) hands it a per-theme dict (Catppuccin Mocha inks for Sunset) and _apply_theme pushes theme flips. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1683,6 +1683,8 @@ class MainWindow(QMainWindow):
|
||||
|
||||
def _apply_theme(self) -> None:
|
||||
self.setStyleSheet(build_app_stylesheet(self._theme_mode))
|
||||
# State colors are painted in code per DAQ tick — QSS can't reach them.
|
||||
self.beamline_state_panel.set_theme(self._theme_mode)
|
||||
|
||||
def _restore_theme_settings(self) -> None:
|
||||
settings = QSettings("PSI", "AareGUI")
|
||||
|
||||
@@ -5,13 +5,7 @@ from PySide6.QtCore import Qt, QTimer, Signal, Slot
|
||||
from PySide6.QtGui import QCursor, QFont, QFontMetrics
|
||||
from PySide6.QtWidgets import QFrame, QHBoxLayout, QLabel, QMenu, QPushButton, QSizePolicy, QToolTip
|
||||
|
||||
from aare.gui.styles import (
|
||||
FONT_VALUE,
|
||||
STATE_AVAILABLE,
|
||||
STATE_MSG_ERROR,
|
||||
STATE_MSG_INFO,
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
from aare.gui.styles import FONT_VALUE, THEME_ORIGINAL, state_colors
|
||||
|
||||
# Shortcut transitions from the "Available transitions" menu in
|
||||
# widgets/status_bar.py show_state_menu — these come ON TOP of the one-hop
|
||||
@@ -146,8 +140,9 @@ class BeamlineStatePanel(QFrame):
|
||||
self._hover_hint_timer.setInterval(3000)
|
||||
self._hover_hint_timer.timeout.connect(self._show_hover_hint)
|
||||
|
||||
self._available_color = STATE_AVAILABLE
|
||||
self._unavailable_color = STATE_UNAVAILABLE
|
||||
# Per-theme colors (MainWindow._apply_theme calls set_theme).
|
||||
self._colors = state_colors(THEME_ORIGINAL)
|
||||
self._separators: list[QLabel] = []
|
||||
|
||||
layout = QHBoxLayout(self)
|
||||
layout.setContentsMargins(10, 2, 10, 2)
|
||||
@@ -159,9 +154,8 @@ class BeamlineStatePanel(QFrame):
|
||||
for index, (state, label) in enumerate(self._ENTRIES):
|
||||
if index:
|
||||
separator = QLabel("–", self)
|
||||
separator.setStyleSheet(
|
||||
f"color: {STATE_UNAVAILABLE}; background: transparent; border: none; font-size: {FONT_VALUE};"
|
||||
)
|
||||
self._style_separator(separator)
|
||||
self._separators.append(separator)
|
||||
layout.addWidget(separator)
|
||||
button = HoverableButton(label, self)
|
||||
button.setFlat(True)
|
||||
@@ -310,6 +304,21 @@ class BeamlineStatePanel(QFrame):
|
||||
elif state == BeamlineStateEnum.XrayFluorescence:
|
||||
self.xray_fluorescence.emit()
|
||||
|
||||
def _style_separator(self, separator: QLabel) -> None:
|
||||
separator.setStyleSheet(
|
||||
f"color: {self._colors['unavailable']};"
|
||||
f" background: transparent; border: none; font-size: {FONT_VALUE};"
|
||||
)
|
||||
|
||||
def set_theme(self, theme: str) -> None:
|
||||
"""Adopt the theme's state colors (MainWindow._apply_theme calls this
|
||||
on every switch — the colors are painted in code, so the app QSS
|
||||
alone cannot restyle them)."""
|
||||
self._colors = state_colors(theme)
|
||||
for separator in self._separators:
|
||||
self._style_separator(separator)
|
||||
self._apply_highlight()
|
||||
|
||||
def _apply_highlight(self) -> None:
|
||||
available = self._available_targets()
|
||||
for state, button in self._buttons.items():
|
||||
@@ -325,14 +334,16 @@ class BeamlineStatePanel(QFrame):
|
||||
# No backgrounds, no rounded corners.
|
||||
if is_current or is_pending:
|
||||
color = (
|
||||
STATE_MSG_ERROR if state == BeamlineStateEnum.Maintenance else STATE_MSG_INFO
|
||||
self._colors["error"]
|
||||
if state == BeamlineStateEnum.Maintenance
|
||||
else self._colors["info"]
|
||||
)
|
||||
bold = True
|
||||
elif is_available:
|
||||
color = self._available_color
|
||||
color = self._colors["available"]
|
||||
bold = False
|
||||
else:
|
||||
color = self._unavailable_color
|
||||
color = self._colors["unavailable"]
|
||||
bold = False
|
||||
|
||||
# Font set in code (not QSS) so _update_label_mode can measure
|
||||
|
||||
+25
-1
@@ -333,11 +333,35 @@ STATUS_VACANT = "#ffff00" # baton vacant (was "yellow")
|
||||
STATUS_REQUEST = "#00ffff" # baton request (was "cyan")
|
||||
|
||||
# -- Beamline state panel ---------------------------------------------------
|
||||
# The panel paints these in code per DAQ tick (data-driven), not via QSS.
|
||||
# The panel paints these in code per DAQ tick (data-driven), so it asks
|
||||
# state_colors(theme) below instead of QSS. Light values unchanged; dark
|
||||
# values are Catppuccin Mocha so they stay readable on the sunset sky.
|
||||
STATE_AVAILABLE = "#ed8936"
|
||||
STATE_UNAVAILABLE = "#8c96a5"
|
||||
STATE_MSG_ERROR = "#c81e1e"
|
||||
STATE_MSG_INFO = "#005caa"
|
||||
DARK_STATE_AVAILABLE = "#fab387" # mocha peach
|
||||
DARK_STATE_UNAVAILABLE = "#7f849c" # mocha overlay1
|
||||
DARK_STATE_MSG_ERROR = "#f38ba8" # mocha red
|
||||
DARK_STATE_MSG_INFO = "#89b4fa" # mocha blue
|
||||
|
||||
|
||||
def state_colors(theme: str) -> dict[str, str]:
|
||||
"""Beamline-state text colors for the given theme."""
|
||||
if theme == THEME_PORTRAIT:
|
||||
return {
|
||||
"available": DARK_STATE_AVAILABLE,
|
||||
"unavailable": DARK_STATE_UNAVAILABLE,
|
||||
"error": DARK_STATE_MSG_ERROR,
|
||||
"info": DARK_STATE_MSG_INFO,
|
||||
}
|
||||
return {
|
||||
"available": STATE_AVAILABLE,
|
||||
"unavailable": STATE_UNAVAILABLE,
|
||||
"error": STATE_MSG_ERROR,
|
||||
"info": STATE_MSG_INFO,
|
||||
}
|
||||
|
||||
|
||||
# -- Sample tables + raster grid --------------------------------------------
|
||||
SAMPLE_ROW_ACTIVE_BG = "#ff6600"
|
||||
|
||||
Reference in New Issue
Block a user