#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ QSS Modern Stylesheet - 现代化UI样式 老王说:样式写好了界面才漂亮,这些都是精心调教过的! """ # 浅色主题 LIGHT_THEME = { "bg_primary": "#ffffff", "bg_secondary": "#fafafa", "bg_tertiary": "#f0f0f0", "bg_card": "#ffffff", "text_primary": "#1a1a1a", "text_secondary": "#595959", "text_muted": "#8c8c8c", "text_placeholder": "#bfbfbf", "border": "#e8e8e8", "border_light": "#f0f0f0", "accent": "#1677ff", "accent_hover": "#4096ff", "accent_pressed": "#0958d9", "accent_bg": "#e6f4ff", "success": "#52c41a", "success_bg": "#f6ffed", "warning": "#faad14", "warning_bg": "#fffbe6", "error": "#ff4d4f", "error_bg": "#fff2f0", "sidebar_bg": "#001529", "sidebar_text": "#ffffffd9", "sidebar_text_muted": "#ffffff73", "sidebar_hover": "#1677ff", "sidebar_active_bg": "#1677ff", "shadow": "rgba(0, 0, 0, 0.08)", } # 深色主题 DARK_THEME = { "bg_primary": "#141414", "bg_secondary": "#1f1f1f", "bg_tertiary": "#262626", "bg_card": "#1f1f1f", "text_primary": "#ffffffd9", "text_secondary": "#ffffffa6", "text_muted": "#ffffff73", "text_placeholder": "#ffffff40", "border": "#424242", "border_light": "#303030", "accent": "#1668dc", "accent_hover": "#3c89e8", "accent_pressed": "#1554ad", "accent_bg": "#111a2c", "success": "#49aa19", "success_bg": "#162312", "warning": "#d89614", "warning_bg": "#2b2111", "error": "#dc4446", "error_bg": "#2c1618", "sidebar_bg": "#000000", "sidebar_text": "#ffffffd9", "sidebar_text_muted": "#ffffff73", "sidebar_hover": "#1668dc", "sidebar_active_bg": "#1668dc", "shadow": "rgba(0, 0, 0, 0.45)", } def get_stylesheet(theme: dict) -> str: """Generate QSS stylesheet based on theme""" return f""" /* ==================== Global Reset ==================== */ * {{ outline: none; }} QWidget {{ font-family: "Microsoft YaHei UI", "Segoe UI", "PingFang SC", sans-serif; font-size: 14px; color: {theme["text_primary"]}; background-color: transparent; }} QMainWindow {{ background-color: {theme["bg_secondary"]}; }} /* ==================== Scroll Area ==================== */ QScrollArea {{ background-color: transparent; border: none; }} QScrollArea > QWidget > QWidget {{ background-color: transparent; }} /* ==================== Sidebar ==================== */ #sidebar {{ background-color: {theme["sidebar_bg"]}; border: none; }} #sidebar QPushButton {{ color: {theme["sidebar_text"]}; background-color: transparent; border: none; border-radius: 0; padding: 14px 20px; text-align: left; font-size: 14px; font-weight: 500; }} #sidebar QPushButton:hover {{ background-color: rgba(255, 255, 255, 0.08); }} #sidebar QPushButton:checked {{ background-color: {theme["sidebar_active_bg"]}; color: white; font-weight: 600; }} #sidebar_title {{ color: white; font-size: 16px; font-weight: bold; padding: 20px; background-color: transparent; }} /* ==================== Buttons ==================== */ QPushButton {{ background-color: {theme["bg_card"]}; color: {theme["text_primary"]}; border: 1px solid {theme["border"]}; border-radius: 6px; padding: 8px 16px; font-weight: 500; }} QPushButton:hover {{ border-color: {theme["accent"]}; color: {theme["accent"]}; }} QPushButton:pressed {{ background-color: {theme["bg_tertiary"]}; }} QPushButton:disabled {{ background-color: {theme["bg_tertiary"]}; color: {theme["text_muted"]}; border-color: {theme["border"]}; }} /* Primary Button */ QPushButton#primary {{ background-color: {theme["accent"]}; color: white; border: none; font-weight: 600; }} QPushButton#primary:hover {{ background-color: {theme["accent_hover"]}; }} QPushButton#primary:pressed {{ background-color: {theme["accent_pressed"]}; }} QPushButton#primary:disabled {{ background-color: {theme["accent"]}; opacity: 0.6; }} /* Success Button */ QPushButton#success {{ background-color: {theme["success"]}; color: white; border: none; }} QPushButton#success:hover {{ background-color: #73d13d; }} /* Danger Button */ QPushButton#danger {{ background-color: {theme["error"]}; color: white; border: none; }} QPushButton#danger:hover {{ background-color: #ff7875; }} /* ==================== Input Fields ==================== */ QLineEdit {{ background-color: {theme["bg_card"]}; color: {theme["text_primary"]}; border: 1px solid {theme["border"]}; border-radius: 6px; padding: 6px 10px; selection-background-color: {theme["accent"]}; selection-color: white; }} QLineEdit:hover {{ border-color: {theme["accent"]}; }} QLineEdit:focus {{ border-color: {theme["accent"]}; border-width: 2px; padding: 5px 9px; }} QLineEdit:disabled {{ background-color: {theme["bg_tertiary"]}; color: {theme["text_muted"]}; }} QLineEdit[readOnly="true"] {{ background-color: {theme["bg_secondary"]}; }} /* ==================== TextEdit ==================== */ QTextEdit, QPlainTextEdit {{ background-color: {theme["bg_card"]}; color: {theme["text_primary"]}; border: 1px solid {theme["border"]}; border-radius: 6px; padding: 10px 14px; selection-background-color: {theme["accent"]}; }} QTextEdit:focus, QPlainTextEdit:focus {{ border-color: {theme["accent"]}; }} /* ==================== ComboBox ==================== */ QComboBox {{ background-color: {theme["bg_card"]}; color: {theme["text_primary"]}; border: 1px solid {theme["border"]}; border-radius: 6px; padding: 6px 10px; padding-right: 30px; }} QComboBox:hover {{ border-color: {theme["accent"]}; }} QComboBox:focus {{ border-color: {theme["accent"]}; }} QComboBox::drop-down {{ border: none; width: 30px; subcontrol-position: right center; }} QComboBox::down-arrow {{ image: none; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 6px solid {theme["text_secondary"]}; margin-right: 10px; }} QComboBox QAbstractItemView {{ background-color: {theme["bg_card"]}; border: 1px solid {theme["border"]}; border-radius: 6px; padding: 4px; selection-background-color: {theme["accent_bg"]}; selection-color: {theme["accent"]}; outline: none; }} QComboBox QAbstractItemView::item {{ padding: 8px 12px; border-radius: 4px; }} QComboBox QAbstractItemView::item:hover {{ background-color: {theme["bg_tertiary"]}; }} /* ==================== SpinBox ==================== */ QSpinBox {{ background-color: {theme["bg_card"]}; color: {theme["text_primary"]}; border: 1px solid {theme["border"]}; border-radius: 6px; padding: 10px 14px; padding-right: 30px; }} QSpinBox:hover {{ border-color: {theme["accent"]}; }} QSpinBox:focus {{ border-color: {theme["accent"]}; }} QSpinBox::up-button, QSpinBox::down-button {{ width: 20px; border: none; background: transparent; }} QSpinBox::up-arrow, QSpinBox::down-arrow {{ width: 0; height: 0; border: none; background: transparent; }} /* ==================== CheckBox ==================== */ QCheckBox {{ spacing: 10px; color: {theme["text_primary"]}; }} QCheckBox::indicator {{ width: 18px; height: 18px; border: 1px solid {theme["border"]}; border-radius: 4px; background-color: {theme["bg_card"]}; }} QCheckBox::indicator:hover {{ border-color: {theme["accent"]}; }} QCheckBox::indicator:checked {{ background-color: {theme["accent"]}; border-color: {theme["accent"]}; }} /* ==================== GroupBox - Card Style ==================== */ QGroupBox {{ background-color: {theme["bg_card"]}; border: 1px solid {theme["border"]}; border-radius: 10px; margin-top: 14px; padding: 24px 0 0 0; }} QGroupBox::title {{ subcontrol-origin: margin; subcontrol-position: top left; left: 16px; top: 0px; padding: 4px 12px; background-color: {theme["accent"]}; color: white; border-radius: 4px; font-weight: 600; font-size: 13px; }} /* ==================== Table ==================== */ QTableWidget {{ background-color: {theme["bg_card"]}; border: 1px solid {theme["border"]}; border-radius: 8px; gridline-color: {theme["border_light"]}; selection-background-color: {theme["accent_bg"]}; selection-color: {theme["text_primary"]}; }} QTableWidget::item {{ padding: 8px 4px; border: none; }} QTableWidget::item:selected {{ background-color: {theme["accent_bg"]}; color: {theme["accent"]}; }} QTableWidget::item:hover {{ background-color: {theme["bg_secondary"]}; }} QHeaderView::section {{ background-color: {theme["bg_secondary"]}; color: {theme["text_secondary"]}; padding: 12px 8px; border: none; border-bottom: 1px solid {theme["border"]}; font-weight: 600; font-size: 13px; }} QHeaderView::section:first {{ border-top-left-radius: 8px; }} QHeaderView::section:last {{ border-top-right-radius: 8px; }} QTableCornerButton::section {{ background-color: {theme["bg_secondary"]}; border: none; }} /* ==================== List ==================== */ QListWidget {{ background-color: {theme["bg_card"]}; border: 1px solid {theme["border"]}; border-radius: 8px; padding: 4px; outline: none; }} QListWidget::item {{ padding: 12px 14px; border-radius: 6px; margin: 2px 0; }} QListWidget::item:selected {{ background-color: {theme["accent_bg"]}; color: {theme["accent"]}; }} QListWidget::item:hover:!selected {{ background-color: {theme["bg_secondary"]}; }} /* ==================== Progress Bar ==================== */ QProgressBar {{ background-color: {theme["bg_tertiary"]}; border: none; border-radius: 4px; height: 8px; text-align: center; }} QProgressBar::chunk {{ background-color: {theme["accent"]}; border-radius: 4px; }} /* ==================== ScrollBar ==================== */ QScrollBar:vertical {{ background-color: transparent; width: 8px; margin: 4px 2px; }} QScrollBar::handle:vertical {{ background-color: {theme["border"]}; border-radius: 4px; min-height: 40px; }} QScrollBar::handle:vertical:hover {{ background-color: {theme["text_muted"]}; }} QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{ height: 0; background: none; }} QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {{ background: none; }} QScrollBar:horizontal {{ background-color: transparent; height: 8px; margin: 2px 4px; }} QScrollBar::handle:horizontal {{ background-color: {theme["border"]}; border-radius: 4px; min-width: 40px; }} QScrollBar::handle:horizontal:hover {{ background-color: {theme["text_muted"]}; }} QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {{ width: 0; background: none; }} /* ==================== Tab Widget ==================== */ QTabWidget::pane {{ border: 1px solid {theme["border"]}; border-radius: 8px; background-color: {theme["bg_card"]}; padding: 8px; }} QTabBar::tab {{ background-color: transparent; color: {theme["text_secondary"]}; padding: 10px 20px; margin-right: 4px; border-bottom: 2px solid transparent; }} QTabBar::tab:selected {{ color: {theme["accent"]}; border-bottom: 2px solid {theme["accent"]}; }} QTabBar::tab:hover:!selected {{ color: {theme["text_primary"]}; }} /* ==================== ToolTip ==================== */ QToolTip {{ background-color: {theme["bg_tertiary"]}; color: {theme["text_primary"]}; border: 1px solid {theme["border"]}; border-radius: 6px; padding: 8px 12px; }} /* ==================== Message Box ==================== */ QMessageBox {{ background-color: {theme["bg_card"]}; }} QMessageBox QLabel {{ color: {theme["text_primary"]}; font-size: 14px; }} QMessageBox QPushButton {{ min-width: 80px; min-height: 32px; }} /* ==================== Dialog ==================== */ QDialog {{ background-color: {theme["bg_card"]}; }} /* ==================== Label ==================== */ QLabel {{ color: {theme["text_primary"]}; background-color: transparent; }} QLabel#title {{ font-size: 20px; font-weight: 600; color: {theme["text_primary"]}; }} QLabel#subtitle {{ font-size: 14px; color: {theme["text_secondary"]}; }} QLabel#help {{ font-size: 13px; color: {theme["text_muted"]}; }} /* ==================== Frame ==================== */ QFrame {{ background-color: transparent; }} QFrame#card {{ background-color: {theme["bg_card"]}; border: 1px solid {theme["border"]}; border-radius: 10px; }} QFrame#separator {{ background-color: {theme["border"]}; max-height: 1px; }} /* ==================== Log Widget ==================== */ #log_widget {{ background-color: {theme["bg_secondary"]}; border-top: 1px solid {theme["border"]}; }} #log_widget QPlainTextEdit {{ background-color: {theme["bg_secondary"]}; border: none; color: {theme["text_secondary"]}; font-family: "Cascadia Code", "Consolas", "Monaco", monospace; font-size: 12px; padding: 8px; }} /* ==================== Status Colors ==================== */ .success {{ color: {theme["success"]}; }} .warning {{ color: {theme["warning"]}; }} .error {{ color: {theme["error"]}; }} /* ==================== Form Label ==================== */ QLabel#formLabel {{ color: {theme["text_secondary"]}; font-weight: 500; }} """ def apply_theme(app, theme_name: str = "light"): """Apply theme to application""" theme = LIGHT_THEME if theme_name == "light" else DARK_THEME app.setStyleSheet(get_stylesheet(theme))