style: centralize the GUI color palette in styles.py

Every color literal in the GUI moves into one palette module: ~200
grouped constants, a qcolor() helper (hue from the theme, alpha at the
call site), and both QSS stylesheets become string.Templates fed by
_palette() so a renamed constant fails loudly. Also retints: light
background #d8e4fd -> #e2e7ee, banner indigo -> #a2b7e4, and the
portrait dark theme adopts Catppuccin Macchiato.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 10:35:46 +02:00
co-authored by Claude Fable 5
parent e818682abd
commit 271ca54d9a
+419 -93
View File
@@ -1,8 +1,312 @@
from __future__ import annotations
from string import Template
THEME_ORIGINAL = "original"
THEME_PORTRAIT = "portrait"
# ---------------------------------------------------------------------------
# Color palette. Change values HERE to try a different look — the QSS below
# and the widgets that import these use them everywhere. Every UPPERCASE
# constant is exposed to the QSS templates as $lowercase_name (see _palette),
# so no color literals live inside the stylesheets themselves.
# Any value can be "transparent" (e.g. to hide a border or a fill).
# string.Template ($name) instead of f-strings so the QSS braces stay literal.
# -- Light theme ------------------------------------------------------------
BACKGROUND = "#e2e7ee"
BANNER = "#a2b7e4"
BANNER_TEXT = "#263043" # must contrast with BANNER
TEXT = "#263043"
SURFACE = "#f2f2f2" # input fields, cards, group boxes
# Borders (all can be "transparent" to hide the line):
BORDER = "transparent" # main dividers, e.g. the beamline state bar top line
CARD_BORDER = "transparent" # cards / group boxes (Local Contact, automation)
COMPACT_BORDER = "transparent" # compact-automation cards and buttons
# Compact-automation page:
COMPACT_CARD_BG = "#e6eefc"
COMPACT_TITLE = "#17324d" # section titles + menu/secondary button text
COMPACT_HINT = "#51657d" # secondary text: hints, queue titles
COMPACT_VALUE = "#10263a"
COMPACT_MENU_BG = "#cbdcf8"
COMPACT_MENU_BG_HOVER = "#bfd4f6"
PRIMARY = "#2563eb" # main action button
PRIMARY_HOVER = "#1d4ed8"
PRIMARY_TEXT = "white"
SECONDARY_BG = "#dfe9fb"
SECONDARY_BG_HOVER = "#d3e1f8"
# Alert banners (alertKind: error / success / waiting=warning):
ERROR_BG = "#fbe4e6"
ERROR_BORDER = "#d97a84"
ERROR_TEXT = "#8f1d2c"
SUCCESS_BG = "#e7f6ea"
SUCCESS_BORDER = "#7bbf8e"
SUCCESS_TEXT = "#1f6a3a"
WARNING_BG = "#fff8e1"
WARNING_BORDER = "#ffb300"
WARNING_TEXT = "#e65100"
# Axis video status + beamline state bar:
STATUS_IDLE_BG = "#d9e2f2"
STATUS_LABEL_TEXT = "#2f3b52"
STATE_TOGGLE_TEXT = "white" # collapse glyph sitting on the BANNER strip
STATE_CURRENT_TEXT = "#1e293b" # same slate as HEADING_TEXT, separate knob
STATE_TELL_TEXT = "#374357" # also the status-bar TELL line
# -- Portrait (dark) theme: Catppuccin Macchiato ----------------------------
# https://catppuccin.com/palette — token names in comments. DARK_BG was set to
# Macchiato mantle by hand, so the rest follows that flavor.
DARK_BG = "#1e2030" # mantle
DARK_TEXT = "#cad3f5" # text
DARK_SURFACE = "#24273a" # base — cards, state panel, scrollbar track
DARK_ELEVATED = "#363a4f" # surface0 — buttons, title strip, idle status pill
DARK_BORDER = "#494d64" # surface1 — card borders, scrollbar handle, button hover fill
DARK_ACCENT = "#8bd5ca" # teal
DARK_ACCENT_HOVER = "#a2ddd5" # teal +20% white — palette has no lighter teal step
DARK_MUTED = "#a5adcb" # subtext0 — secondary text
# Alert banners: full-strength color for border/text, 25%-over-DARK_BG tint
# for bg (Catppuccin defines no alert backgrounds, so these are blends).
DARK_ERROR_BG = "#523a4a" # red 25% over mantle
DARK_ERROR_BORDER = "#ed8796" # red
DARK_ERROR_TEXT = "#ed8796" # red
DARK_SUCCESS_BG = "#404e49" # green 25% over mantle
DARK_SUCCESS_BORDER = "#a6da95" # green
DARK_SUCCESS_TEXT = "#a6da95" # green
DARK_WARNING_BG = "#524d4c" # yellow 25% over mantle
DARK_WARNING_BORDER = "#eed49f" # yellow
DARK_WARNING_TEXT = "#eed49f" # yellow
# -- Shared chrome (light-theme widgets) ------------------------------------
# Extracted from per-widget literals so the whole app is themeable from this
# file. The same hex may appear under two names when the roles differ —
# separate knobs on purpose.
WHITE = "#ffffff"
DEFAULT_TEXT = "#000000" # labels that reset to plain black
NOTE_TEXT = "#555555" # tutorial hints, TELL sample details
DIM_TEXT = "#666666" # baton dialog timers
HINT_TEXT = "#999999" # baton dialog fine print
HEADING_TEXT = "#1e293b" # card headings (slate-800)
SUBTLE_TEXT = "#334155" # card body text (slate-700)
MUTED_TEXT = "#475569" # neutral chip / idle step text (slate-600)
FAINT_TEXT = "#64748b" # pending/skipped step text (slate-500)
SHADOW = "#000000" # drop shadows & tutorial scrim; alpha stays at call site
# -- Semantic action colors -------------------------------------------------
GO_TEXT = "#4e9a06" # green start/run/measure button text
ABORT_TEXT = "#a40000" # abort button text
ALERT_TEXT = "#ff0000" # out-of-range motor labels
PATH_WARN_TEXT = "#c80000" # file-exists warning in path panel
DANGER_ACCENT = "#d9534f" # invalid p-group border + message text
# -- Status chips (local contact status) — "good" reuses SUCCESS_BG/TEXT ----
CHIP_WARN_BG = "#fff3cd"
CHIP_WARN_TEXT = "#7a4b00"
CHIP_BAD_BG = "#fdeaea"
CHIP_BAD_TEXT = "#8b1e1e"
CHIP_NEUTRAL_BG = "#e9eef5" # text uses MUTED_TEXT
CHIP_INFO_BG = "#e8f1ff"
CHIP_INFO_TEXT = "#12406a"
# -- Status cards (beamline recovery, local contact error frame) ------------
WARN_CARD_BORDER = "#f0c36d"
BAD_CARD_BORDER = "#e6a8a8"
INFO_CARD_BG = "#eef6ff"
INFO_CARD_BORDER = "#a8c7e6"
PENDING_CARD_BG = "#fff7db"
PENDING_CARD_BORDER = "#e7cb73"
# -- Log panel --------------------------------------------------------------
LOG_BORDER = "#8a8a8a"
LOG_PANEL_BG = "#fff4f4"
LOG_ERROR_BG = "#fff1f1"
LOG_ERROR_BORDER = "#dd6666"
LOG_WARN_BG = "#fff8e8"
LOG_WARN_BORDER = "#d7aa42"
LOG_SUCCESS_BG = "#eefaf0"
LOG_SUCCESS_BORDER = "#6cb37a"
LOG_INFO_BG = "#eef5ff"
LOG_INFO_BORDER = "#6b9bd6"
# -- Automation panel + progress steps --------------------------------------
AUTOMATION_TITLE_TEXT = "#1f2937"
AUTOMATION_HINT_TEXT = "#374151"
STEP_RUNNING_TEXT = "#2563eb" # same blue as PRIMARY, separate knob
STEP_SUCCESS_TEXT = "#15803d"
STEP_FAILED_TEXT = "#b91c1c"
STEP_PAUSED_TEXT = "#c2410c"
STEP_DONE_BG = "#ecfdf3"
STEP_DONE_TEXT = "#166534"
STEP_DONE_BORDER = "#a7f3d0"
STEP_ACTIVE_BG = "#eff6ff"
STEP_ACTIVE_TEXT = "#1d4ed8"
STEP_ACTIVE_BORDER = "#bfdbfe"
STEP_FAILED_BG = "#fef2f2"
STEP_FAILED_BORDER = "#fecaca"
STEP_PAUSED_BG = "#fff7ed"
STEP_PAUSED_BORDER = "#fed7aa"
STEP_IDLE_BG = "#f8fafc"
STEP_IDLE_BORDER = "#e2e8f0"
# -- Baton request dialog ---------------------------------------------------
BATON_OK_BG = "#4caf50"
BATON_OK_HOVER = "#45a049"
BATON_OK_PRESSED = "#3d8b40"
BATON_DANGER_BG = "#f44336"
BATON_DANGER_HOVER = "#da190b"
BATON_DANGER_PRESSED = "#c41000"
BATON_WARN = "#ff9800"
BATON_INFO = "#2196f3"
LIGHT_BORDER = "#cccccc"
PROGRESS_TRACK_BG = "#f0f0f0"
# -- Splash screen ----------------------------------------------------------
SPLASH_BG = "#222222"
SPLASH_BORDER = "#444444"
SPLASH_ACCENT = "#0078d7"
# -- Numeric inputs ---------------------------------------------------------
INPUT_BG = "#ffffff"
INPUT_INVALID_BG = "#ffd5d5"
INPUT_DISABLED_BG = "#f0f0f0"
INPUT_DISABLED_INVALID_BG = "#f0e1e1"
# -- Status bar flags (hex equivalents of the old CSS named colors) ---------
STATUS_OK = "#008000" # closed / idle / owned / tell ok (was "green")
STATUS_ALERT = "#ff0000" # open / busy / other-owner / hot cryo (was "red")
STATUS_WARN = "#ffa500" # baton waiting / warming cryo / tell busy (was "orange")
STATUS_INFO = "#0000ff" # cold cryo (was "blue")
STATUS_VACANT = "#ffff00" # baton vacant (was "yellow")
STATUS_REQUEST = "#00ffff" # baton request (was "cyan")
# -- Beamline state panel ---------------------------------------------------
STATE_AVAILABLE = "#ed8936"
STATE_UNAVAILABLE = "#8c96a5"
STATE_MSG_ERROR = "#c81e1e"
STATE_MSG_INFO = "#005caa"
# -- Sample tables + raster grid --------------------------------------------
SAMPLE_ROW_ACTIVE_BG = "#ff6600"
SAMPLE_ROW_QUEUED_BG = "#729fcf"
SAMPLE_ROW_HIGHLIGHT_BG = "#d8e4fd"
RASTER_GRID_LINE = "#729fcf"
TABLE_SHADE_BG = "#e0e0e0"
# -- Camera / video overlay (painter colors, alpha at call site) ------------
BEAM_OPEN = "#00ff00" # beam marker: shutter open
BEAM_IDLE = "#f57900" # beam marker: idle
BEAM_BUSY = "#ff0000" # beam marker: busy
BEAM_MARKING = "#663399" # beam marker: marking mode
MARKER_GREEN = "#32cd32" # loop-centering click marker
PATH_START = "#008000" # raster path gradient start + start circle
PATH_END = "#ff0000" # raster path gradient end + end circle
LEGEND_BG = "#141414"
LEGEND_TEXT = "#f0f0f0"
TOOLTIP_TEXT = "#e6e6e6"
MARK_TOOLTIP_GOLD = "#ffd700"
MARK_TOOLTIP_ORANGE = "#ffa500"
MARK_TOOLTIP_RED = "#ff0000"
MARK_BADGE_BG = "#b43c00"
# Prediction class overlay colors. The chart variant historically used pure
# green (#00ff00) while the overlay used CSS green (#008000) — both kept.
CLASS_COLORS = {
"pin": "#ff0000",
"loop_all": "#008000",
"loop_face": "#ffff00",
"crystal": "#0000ff",
"needle": "#ff00ff",
"ice": "#00ffff",
}
CHART_CLASS_COLORS = {
"Pin": "#ff0000",
"Loop_all": "#00ff00",
"Loop_face": "#ffff00",
"Crystal": "#0000ff",
"Needle": "#ff00ff",
"Ice": "#00ffff",
}
TARGET_COLORS = {"Cyan": "#00ffff", "Dark Blue": "#0046a0", "Dark Red": "#8c1919"}
BOOKMARK_COLORS = {
"red": "#ff0000",
"green": "#008000",
"blue": "#0000ff",
"indigo": "#4b0082",
"lime": "#00ff00",
}
# -- Busy overlay (per-source color coding) ---------------------------------
BUSY_YELLOW = "#f1c40f"
BUSY_YELLOW_BORDER = "#fff8d2"
BUSY_YELLOW_DOT = "#fff6bf"
BUSY_YELLOW_TEXT_DARK = "#3b2f00"
BUSY_PURPLE = "#8e44ad"
BUSY_PURPLE_BORDER = "#ebdcf5"
BUSY_PURPLE_DOT = "#f0dfff"
BUSY_RED_BADGE = "#d64545"
BUSY_RED_FILL = "#be2828"
BUSY_RED_BORDER = "#ffdcdc"
BUSY_RED_DOT = "#ffdddd"
BUSY_ORANGE = "#e67e22"
BUSY_ORANGE_BORDER = "#ffead6"
BUSY_ORANGE_DOT = "#fff0db"
BUSY_BLUE = "#3498db"
BUSY_BLUE_BORDER = "#dcf0ff"
BUSY_BLUE_DOT = "#dff2ff"
BUSY_PSI_RED = "#e04f39"
BUSY_PSI_RED_BORDER = "#ffe1dc"
BUSY_PSI_RED_DOT = "#ffd8d1"
# -- Charts (prediction metrics, target stability, fluorescence) ------------
CHART_BLUE = "#1f77b4"
CHART_BLUE_LIGHT = "#6baed6"
CHART_BLUE_PALE = "#9ecae1"
CHART_RED = "#d62728"
CHART_RED_LIGHT = "#ff9896"
CHART_RED_DARK = "#c43c39"
CHART_ORANGE = "#ff7f0e"
CHART_ORANGE_PALE = "#ffbb78"
CHART_GREEN = "#2ca02c"
CHART_GREEN_PALE = "#98df8a"
CHART_CYAN = "#17becf"
CHART_PURPLE = "#9467bd"
CHART_MUTED = "#888888"
CONFIDENCE_BIN_COLORS = [CHART_RED, CHART_ORANGE, CHART_ORANGE_PALE, CHART_GREEN_PALE, CHART_GREEN]
SPECTRUM_LINE = "#cc0000"
# -- Generic panels (developer help, raster table) --------------------------
PANEL_BG_SOFT = "#f6f6f6"
PANEL_BG_FAINT = "#fafafa"
PANEL_BORDER = "#d0d0d0"
PANEL_BORDER_DARK = "#bdbdbd"
PANEL_BORDER_LIGHT = "#e0e0e0"
TUTORIAL_BORDER = "#555555"
TUTORIAL_HIGHLIGHT = "#ffff00" # widget spotlight pen (was Qt.yellow)
# ---------------------------------------------------------------------------
def _palette() -> dict[str, str]:
# Why: one substitution mapping instead of ~40 kwargs per stylesheet, and
# a renamed/missing constant fails loudly (KeyError) instead of silently.
return {k.lower(): v for k, v in globals().items() if k.isupper() and isinstance(v, str)}
def qcolor(color: str, alpha: int | None = None):
"""QColor from a palette hex string, optional 0-255 alpha.
Hue lives here (theme), alpha stays at the call site (behavior). Lazy Qt
import so this module — and its self-check — stay importable without
PySide6.
"""
from PySide6.QtGui import QColor
c = QColor(color)
if alpha is not None:
c.setAlpha(alpha)
return c
def build_app_stylesheet(theme: str) -> str:
if theme == THEME_PORTRAIT:
@@ -11,24 +315,24 @@ def build_app_stylesheet(theme: str) -> str:
def _original_stylesheet() -> str:
return """
return Template("""
QMainWindow, QWidget {
background-color: rgb(216, 228, 253);
color: rgb(30, 41, 59);
background-color: $background;
color: $text;
}
QWidget#mainContentRoot,
QWidget#standardMainPage,
QWidget#compactAutomationPage {
background-color: rgb(216, 228, 253);
background-color: $background;
}
QWidget#portraitModePage {
background-color: #071018;
background-color: $dark_bg;
}
QFrame#compactAutomationPanel {
background: #d8e4fd;
background: $background;
border: none;
border-radius: 18px;
}
@@ -38,42 +342,42 @@ def _original_stylesheet() -> str:
QFrame#compactProgressCard,
QFrame#compactQueueCard,
QFrame#compactQueueItem {
background: #e6eefc;
border: 1px solid #b9ccee;
background: $compact_card_bg;
border: 1px solid $compact_border;
border-radius: 16px;
}
QLabel#compactSectionTitle {
background: transparent;
color: #17324d;
color: $compact_title;
font-size: 14px;
font-weight: 700;
}
QLabel#compactSectionHint {
background: transparent;
color: #51657d;
color: $compact_hint;
font-size: 12px;
}
QLabel#compactQueueTitle {
background: transparent;
color: #51657d;
color: $compact_hint;
font-size: 11px;
font-weight: 700;
}
QLabel#compactQueueValue {
background: transparent;
color: #10263a;
color: $compact_value;
font-size: 14px;
font-weight: 700;
}
QToolButton#compactMenuButton {
background: #cbdcf8;
color: #17324d;
border: 1px solid #9fb9e5;
background: $compact_menu_bg;
color: $compact_title;
border: 1px solid $compact_border;
border-radius: 14px;
padding: 10px 14px;
font-size: 18px;
@@ -81,12 +385,12 @@ def _original_stylesheet() -> str:
}
QToolButton#compactMenuButton:hover {
background: #bfd4f6;
background: $compact_menu_bg_hover;
}
QPushButton#compactPrimaryButton {
background: #2563eb;
color: white;
background: $primary;
color: $primary_text;
border: none;
border-radius: 14px;
padding: 14px 18px;
@@ -95,14 +399,14 @@ def _original_stylesheet() -> str:
}
QPushButton#compactPrimaryButton:hover {
background: #1d4ed8;
background: $primary_hover;
}
QPushButton#compactSecondaryButton,
QToolButton#compactSecondaryButton {
background: #dfe9fb;
color: #17324d;
border: 1px solid #b2c7eb;
background: $secondary_bg;
color: $compact_title;
border: 1px solid $compact_border;
border-radius: 14px;
padding: 14px 18px;
font-size: 14px;
@@ -111,26 +415,26 @@ def _original_stylesheet() -> str:
QPushButton#compactSecondaryButton:hover,
QToolButton#compactSecondaryButton:hover {
background: #d3e1f8;
background: $secondary_bg_hover;
}
QFrame#alertBanner[alertKind="error"] {
background-color: #fbe4e6;
border: 2px solid #d97a84;
background-color: $error_bg;
border: 2px solid $error_border;
border-radius: 12px;
margin: 8px 12px 8px 12px;
}
QFrame#alertBanner[alertKind="success"] {
background-color: #e7f6ea;
border: 2px solid #7bbf8e;
background-color: $success_bg;
border: 2px solid $success_border;
border-radius: 12px;
margin: 8px 12px 8px 12px;
}
QFrame#alertBanner[alertKind="waiting"] {
background-color: #fff8e1;
border: 2px solid #ffb300;
background-color: $warning_bg;
border: 2px solid $warning_border;
border-radius: 12px;
margin: 8px 12px 8px 12px;
}
@@ -142,20 +446,20 @@ def _original_stylesheet() -> str:
}
QFrame#alertBanner[alertKind="error"] QLabel {
color: #8f1d2c;
color: $error_text;
}
QFrame#alertBanner[alertKind="success"] QLabel {
color: #1f6a3a;
color: $success_text;
}
QFrame#alertBanner[alertKind="waiting"] QLabel {
color: #e65100;
color: $warning_text;
}
QWidget#axisVideoStatusContainer[busyState="idle"] {
border-radius: 12px;
background-color: #d9e2f2;
background-color: $status_idle_bg;
}
QWidget#axisVideoStatusContainer[busyState="active"] {
@@ -173,18 +477,33 @@ def _original_stylesheet() -> str:
QLabel#axisVideoStatusLabel {
background-color: transparent;
color: #2f3b52;
color: $status_label_text;
font-weight: bold;
}
QTabWidget::pane {
border: 1px solid $border;
}
QMainWindow::separator {
background: $border;
width: 4px;
height: 4px;
}
QFrame#beamlineControls,
QFrame#dataCollectionSettings {
border: 1px solid $border;
}
QFrame#beamlineStatePanel {
background: rgb(216, 228, 253);
border-top: 1px solid rgb(185, 204, 238);
background: $background;
border-top: 1px solid $border;
}
QLabel#beamlineStateTitle {
background-color: #4B0082;
color: #ffffff;
background-color: $banner;
color: $banner_text;
font-size: 16px;
font-weight: 700;
}
@@ -193,13 +512,13 @@ def _original_stylesheet() -> str:
QPushButton#beamlineStateToggleButton {
border: none;
background: transparent;
color: white;
color: $state_toggle_text;
font-size: 14px;
font-weight: 700;
}
QLabel#beamlineStateCurrentLabel {
color: rgb(30, 41, 59);
color: $state_current_text;
font-size: 18px;
font-weight: 700;
padding-left: 4px;
@@ -207,7 +526,7 @@ def _original_stylesheet() -> str:
}
QLabel#beamlineStateTellLabel {
color: rgb(55, 67, 87);
color: $state_tell_text;
font-size: 15px;
font-weight: 600;
padding-left: 4px;
@@ -216,8 +535,8 @@ def _original_stylesheet() -> str:
QWidget#portraitRoot,
QWidget#portraitRoot QWidget {
background: #071018;
color: #F5F7FA;
background: $dark_bg;
color: $dark_text;
font-family: 'Inter', 'SF Pro Display', Arial, sans-serif;
font-size: 14px;
}
@@ -228,13 +547,13 @@ def _original_stylesheet() -> str:
}
QWidget#portraitRoot QScrollBar:vertical {
background: #0E1A26;
background: $dark_surface;
width: 4px;
border-radius: 2px;
}
QWidget#portraitRoot QScrollBar::handle:vertical {
background: #1A3A36;
background: $dark_border;
border-radius: 2px;
min-height: 20px;
}
@@ -243,33 +562,33 @@ def _original_stylesheet() -> str:
QWidget#portraitRoot QScrollBar::sub-line:vertical {
height: 0px;
}
"""
""").substitute(_palette())
def _portrait_stylesheet() -> str:
return """
return Template("""
QMainWindow, QWidget {
background: #071018;
color: #F5F7FA;
background: $dark_bg;
color: $dark_text;
}
QWidget#mainContentRoot,
QWidget#standardMainPage,
QWidget#compactAutomationPage,
QWidget#portraitModePage {
background: #071018;
background: $dark_bg;
}
QTabWidget::pane,
QScrollArea,
QDockWidget,
QDockWidget > QWidget {
background: #071018;
color: #F5F7FA;
background: $dark_bg;
color: $dark_text;
}
QFrame#compactAutomationPanel {
background: #071018;
background: $dark_bg;
border: none;
border-radius: 18px;
}
@@ -279,42 +598,42 @@ def _portrait_stylesheet() -> str:
QFrame#compactProgressCard,
QFrame#compactQueueCard,
QFrame#compactQueueItem {
background: #0E1A26;
border: 1px solid #1A3A36;
background: $dark_surface;
border: 1px solid $dark_border;
border-radius: 16px;
}
QLabel#compactSectionTitle {
background: transparent;
color: #62D8C8;
color: $dark_accent;
font-size: 14px;
font-weight: 700;
}
QLabel#compactSectionHint {
background: transparent;
color: #8A9BB0;
color: $dark_muted;
font-size: 12px;
}
QLabel#compactQueueTitle {
background: transparent;
color: #8A9BB0;
color: $dark_muted;
font-size: 11px;
font-weight: 700;
}
QLabel#compactQueueValue {
background: transparent;
color: #F5F7FA;
color: $dark_text;
font-size: 14px;
font-weight: 700;
}
QToolButton#compactMenuButton {
background: #132131;
color: #62D8C8;
border: 1px solid #1A3A36;
background: $dark_elevated;
color: $dark_accent;
border: 1px solid $dark_border;
border-radius: 14px;
padding: 10px 14px;
font-size: 18px;
@@ -322,12 +641,12 @@ def _portrait_stylesheet() -> str:
}
QToolButton#compactMenuButton:hover {
background: #1A3A36;
background: $dark_border;
}
QPushButton#compactPrimaryButton {
background: #62D8C8;
color: #071018;
background: $dark_accent;
color: $dark_bg;
border: none;
border-radius: 14px;
padding: 14px 18px;
@@ -336,14 +655,14 @@ def _portrait_stylesheet() -> str:
}
QPushButton#compactPrimaryButton:hover {
background: #7ce6d8;
background: $dark_accent_hover;
}
QPushButton#compactSecondaryButton,
QToolButton#compactSecondaryButton {
background: #132131;
color: #F5F7FA;
border: 1px solid #1A3A36;
background: $dark_elevated;
color: $dark_text;
border: 1px solid $dark_border;
border-radius: 14px;
padding: 14px 18px;
font-size: 14px;
@@ -352,26 +671,26 @@ def _portrait_stylesheet() -> str:
QPushButton#compactSecondaryButton:hover,
QToolButton#compactSecondaryButton:hover {
background: #1A3A36;
background: $dark_border;
}
QFrame#alertBanner[alertKind="error"] {
background: #1A0E0E;
border: 2px solid #8f1d2c;
background: $dark_error_bg;
border: 2px solid $dark_error_border;
border-radius: 12px;
margin: 8px 12px 8px 12px;
}
QFrame#alertBanner[alertKind="success"] {
background: #0E1A12;
border: 2px solid #2a7a44;
background: $dark_success_bg;
border: 2px solid $dark_success_border;
border-radius: 12px;
margin: 8px 12px 8px 12px;
}
QFrame#alertBanner[alertKind="waiting"] {
background: #2B2208;
border: 2px solid #ffb300;
background: $dark_warning_bg;
border: 2px solid $dark_warning_border;
border-radius: 12px;
margin: 8px 12px 8px 12px;
}
@@ -383,20 +702,20 @@ def _portrait_stylesheet() -> str:
}
QFrame#alertBanner[alertKind="error"] QLabel {
color: #ffb3bc;
color: $dark_error_text;
}
QFrame#alertBanner[alertKind="success"] QLabel {
color: #a8f0c0;
color: $dark_success_text;
}
QFrame#alertBanner[alertKind="waiting"] QLabel {
color: #ffd166;
color: $dark_warning_text;
}
QWidget#axisVideoStatusContainer[busyState="idle"] {
border-radius: 12px;
background-color: #132131;
background-color: $dark_elevated;
}
QWidget#axisVideoStatusContainer[busyState="active"] {
@@ -414,18 +733,18 @@ def _portrait_stylesheet() -> str:
QLabel#axisVideoStatusLabel {
background-color: transparent;
color: #8A9BB0;
color: $dark_muted;
font-weight: bold;
}
QFrame#beamlineStatePanel {
background: #0E1A26;
border-top: 1px solid #1A3A36;
background: $dark_surface;
border-top: 1px solid $dark_border;
}
QLabel#beamlineStateTitle {
background-color: #132131;
color: #F5F7FA;
background-color: $dark_elevated;
color: $dark_text;
font-size: 16px;
font-weight: 700;
}
@@ -434,13 +753,13 @@ def _portrait_stylesheet() -> str:
QPushButton#beamlineStateToggleButton {
border: none;
background: transparent;
color: #F5F7FA;
color: $dark_text;
font-size: 14px;
font-weight: 700;
}
QLabel#beamlineStateCurrentLabel {
color: #F5F7FA;
color: $dark_text;
font-size: 18px;
font-weight: 700;
padding-left: 4px;
@@ -448,7 +767,7 @@ def _portrait_stylesheet() -> str:
}
QLabel#beamlineStateTellLabel {
color: #8A9BB0;
color: $dark_muted;
font-size: 15px;
font-weight: 600;
padding-left: 4px;
@@ -457,8 +776,8 @@ def _portrait_stylesheet() -> str:
QWidget#portraitRoot,
QWidget#portraitRoot QWidget {
background: #071018;
color: #F5F7FA;
background: $dark_bg;
color: $dark_text;
font-family: 'Inter', 'SF Pro Display', Arial, sans-serif;
font-size: 14px;
}
@@ -469,13 +788,13 @@ def _portrait_stylesheet() -> str:
}
QWidget#portraitRoot QScrollBar:vertical {
background: #0E1A26;
background: $dark_surface;
width: 4px;
border-radius: 2px;
}
QWidget#portraitRoot QScrollBar::handle:vertical {
background: #1A3A36;
background: $dark_border;
border-radius: 2px;
min-height: 20px;
}
@@ -484,4 +803,11 @@ def _portrait_stylesheet() -> str:
QWidget#portraitRoot QScrollBar::sub-line:vertical {
height: 0px;
}
"""
""").substitute(_palette())
if __name__ == "__main__":
# ponytail: smallest check that fails if a $name has no matching constant
for _theme in (THEME_ORIGINAL, THEME_PORTRAIT):
assert "$" not in build_app_stylesheet(_theme)
print("gude")