From 9c1772b186adb4dcc7ad475224080fd0997dc9b3 Mon Sep 17 00:00:00 2001 From: KenanZhu <3471685733@qq.com> Date: Sat, 30 May 2026 19:27:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(theme):=20=E6=96=B0=E5=A2=9E=20BlueForest?= =?UTF-8?q?=20=E5=AE=98=E6=96=B9=E6=B7=B1=E8=89=B2=E4=B8=BB=E9=A2=98?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 BlueForest.qss,基于 Fusion 控件规格的纯配色深色主题 - 深蓝底色 + 亮青绿强调色,控件尺寸与 Fusion 风格保持一致 - 全局统一 selection-background-color 为 #2dd4bf - 背景色分层:页面 > 头部栏 > 交互控件 > 弹出层 > 输入区 - Border 属性统一拆分为 style/color/width 三段式 - AppInitializer / ALSettingsWidget 配合主题加载 Co-Authored-By: Claude Opus 4.8 --- src/boot/AppInitializer.py | 9 +- src/gui/ALSettingsWidget.py | 49 ++- src/gui/resources/themes/BlueForest.qss | 427 +++++++++++++++++++ src/gui/resources/ui/ALTimerTaskAddDialog.ui | 4 +- 4 files changed, 468 insertions(+), 21 deletions(-) create mode 100644 src/gui/resources/themes/BlueForest.qss diff --git a/src/boot/AppInitializer.py b/src/boot/AppInitializer.py index 4c0440e..11f8a50 100644 --- a/src/boot/AppInitializer.py +++ b/src/boot/AppInitializer.py @@ -12,7 +12,11 @@ import os from PySide6.QtCore import QStandardPaths, QDir from PySide6.QtWidgets import QApplication -from gui.ALSettingsWidget import _applyTheme +from gui.ALSettingsWidget import ( + _setActiveStyleName, + _applyTheme, + _applyQss, +) from interfaces.ConfigProvider import CfgKey from managers.config.ConfigManager import instance as configInstance from managers.driver.WebDriverManager import instance as webdriverInstance @@ -76,7 +80,10 @@ def _initializeAppearance( cfg = configInstance() saved_style = cfg.get(CfgKey.GLOBAL.APPEARANCE.STYLE, "Fusion") saved_theme = cfg.get(CfgKey.GLOBAL.APPEARANCE.THEME, "system") + saved_qss = cfg.get(CfgKey.GLOBAL.APPEARANCE.CUSTOM_QSS, "") app.setStyle(saved_style) + _setActiveStyleName(saved_style) + _applyQss(saved_qss) _applyTheme(saved_theme) def initializeApp( diff --git a/src/gui/ALSettingsWidget.py b/src/gui/ALSettingsWidget.py index 1fe44fb..6ce37d2 100644 --- a/src/gui/ALSettingsWidget.py +++ b/src/gui/ALSettingsWidget.py @@ -26,7 +26,6 @@ from PySide6.QtWidgets import ( QApplication, QFileDialog, QMessageBox, - QStyle, QStyleFactory, QWidget ) @@ -40,6 +39,16 @@ from interfaces.ConfigProvider import ( ) +_active_style_name = "" + + +def _setActiveStyleName( + name: str +): + + global _active_style_name + _active_style_name = name + def _clearQss( ): @@ -76,6 +85,7 @@ def _applyTheme( theme: str ): + global _active_style_name app : QApplication | None = QApplication.instance() if not app: return @@ -85,7 +95,7 @@ def _applyTheme( app.styleHints().setColorScheme(Qt.ColorScheme.Light) else: app.styleHints().setColorScheme(Qt.ColorScheme.Unknown) - app.setStyle(QStyleFactory.create(app.style().objectName())) + app.setStyle(QStyleFactory.create(_active_style_name)) def _restartApp( ): @@ -104,7 +114,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget): ): super().__init__(parent) self.__cfg_mgr: ConfigProvider = ConfigManager.instance() - self.__original_style: QStyle | None = None + self.__original_style: str = "" self.setupUi(self) self.modifyUi() @@ -137,6 +147,12 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget): self.StyleComboBox.clear() self.StyleComboBox.addItems(QStyleFactory.keys()) + def currentStyleKey( + self + ) -> str: + + return _active_style_name + def connectSignals( self ): @@ -184,7 +200,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget): theme = self.__cfg_mgr.get(CfgKey.GLOBAL.APPEARANCE.THEME, "system") style = self.__cfg_mgr.get(CfgKey.GLOBAL.APPEARANCE.STYLE, "Fusion") custom_qss = self.__cfg_mgr.get(CfgKey.GLOBAL.APPEARANCE.CUSTOM_QSS, "") - self.__original_style = QApplication.instance().style() + self.__original_style = self.currentStyleKey() if theme == "light": self.LightThemeRadio.setChecked(True) elif theme == "dark": @@ -192,10 +208,9 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget): else: self.SystemThemeRadio.setChecked(True) index = self.StyleComboBox.findText(style) - if index >= 0: - self.StyleComboBox.setCurrentIndex(index) - else: - self.StyleComboBox.setCurrentIndex(0) + if index < 0: + index = 0 + self.StyleComboBox.setCurrentIndex(index) self.QssPathEdit.setText(custom_qss) self.updateQssStatus(custom_qss) @@ -205,7 +220,8 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget): ): if qss_path and os.path.isfile(qss_path): - self.QssStatusLabel.setText(f"已加载自定义样式文件:{qss_path}") + filename = os.path.basename(qss_path) + self.QssStatusLabel.setText(f"已加载自定义样式文件:{filename}") else: self.QssStatusLabel.setText("当前使用程序默认外观。") @@ -219,7 +235,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget): theme = "dark" else: theme = "system" - style = QStyleFactory.create(self.StyleComboBox.currentText()) + style = self.StyleComboBox.currentText() custom_qss = self.QssPathEdit.text().strip() return theme, style, custom_qss @@ -229,16 +245,13 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget): theme, style, custom_qss = self.collectSettings() self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.THEME, theme) - self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.STYLE, style.name()) + self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.STYLE, style) self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.CUSTOM_QSS, custom_qss) - if custom_qss and os.path.isfile(custom_qss): - _applyQss(custom_qss) - else: - _clearQss() + _applyQss(custom_qss) _applyTheme(theme) self.setNavigationIcons() self.updateQssStatus(custom_qss) - self.__original_style = QApplication.instance().style() + self.__original_style = self.currentStyleKey() def maybeRestart( self @@ -322,7 +335,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget): ): _, style, _ = self.collectSettings() - style_changed = self.__original_style.name() != style.name() + style_changed = self.__original_style != style self.saveAndApply() if style_changed: self.maybeRestart() @@ -333,7 +346,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget): ): _, style, _ = self.collectSettings() - style_changed = self.__original_style.name() != style.name() + style_changed = self.__original_style != style self.saveAndApply() if style_changed: self.maybeRestart() diff --git a/src/gui/resources/themes/BlueForest.qss b/src/gui/resources/themes/BlueForest.qss new file mode 100644 index 0000000..b984848 --- /dev/null +++ b/src/gui/resources/themes/BlueForest.qss @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2026 KenanZhu. + * All rights reserved. + * + * This software is provided "as is", without any warranty of any kind. + * You may use, modify, and distribute this file under the terms of the MIT License. + * See the LICENSE file for details. + * + * + * AutoLibrary Official Style Theme : BlueForest + */ + +/* ---- Global ---- */ +QWidget { + background-color: #0f1a2e; + color: #d0daf0; + selection-background-color: #2dd4bf; + selection-color: #0f1119; +} +QMainWindow::separator { + background-color: #1c2840; + width: 1px; + height: 1px; +} + +/* ---- Menu Bar ---- */ +QMenuBar { + background-color: #0f1628; + border-bottom: 1px solid #1c2840; + padding: 2px 6px; + color: #d0daf0; +} +QMenuBar::item { + padding: 4px 10px; + border-radius: 4px; +} +QMenuBar::item:selected { + background-color: #1c2840; +} +QMenu { + background-color: #162038; + border-style: solid; + border-color: #253250; + border-width: 1px; + padding: 4px; + border-radius: 6px; +} +QMenu::item { + padding: 5px 15px 5px 10px; + border-radius: 4px; +} +QMenu::item:selected { + background-color: #2dd4bf; + color: #0f1119; +} +QMenu::separator { + height: 1px; + background-color: #253250; + margin: 4px 8px; +} + +/* ---- Button ---- */ +QPushButton { + border-style: solid; + border-color: #253250; + border-width: 1px; + border-radius: 5px; + color: #d0daf0; + padding: 2px; + background-color: #1c2840; +} +QPushButton:hover { + background-color: #243458; + border-color: #334478; +} +QPushButton:pressed { + background-color: #162038; + border-color: #2dd4bf; +} +QPushButton:disabled { + background-color: #162038; + color: #5568a0; + border-color: #1c2840; +} +QPushButton[default="true"] { + background-color: #2dd4bf; + color: #0f1119; + border-color: #2dd4bf; +} +QPushButton[default="true"]:hover { + background-color: #3de0cc; +} + +/* ---- Input ---- */ +QLineEdit, +QPlainTextEdit, +QTextEdit, +QSpinBox, +QDoubleSpinBox, +QDateEdit, +QTimeEdit { + background-color: #0a1020; + border-style: solid; + border-color: #253250; + border-width: 1px; + border-radius: 5px; + padding: 4px 8px; + color: #d0daf0; + selection-background-color: #2dd4bf; + selection-color: #0f1119; +} +QLineEdit:focus, +QPlainTextEdit:focus, +QTextEdit:focus, +QSpinBox:focus, +QDoubleSpinBox:focus, +QDateEdit:focus, +QTimeEdit:focus { + border-color: #2dd4bf; +} +QPlainTextEdit, +QTextEdit { + background-color: #0a1020; +} + +/* ---- Combo Box ---- */ +QComboBox { + background-color: #1c2840; + border-style: solid; + border-color: #253250; + border-width: 1px; + border-radius: 5px; + padding: 4px 10px; + color: #d0daf0; +} +QComboBox:hover { + border-color: #334478; +} +QComboBox:focus { + border-color: #2dd4bf; +} +QComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 24px; + border-left: 1px solid #253250; + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; +} +QComboBox::down-arrow { + image: none; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 6px solid #7888b8; + margin-right: 6px; +} +QComboBox QAbstractItemView { + background-color: #162038; + border-style: solid; + border-color: #253250; + border-width: 1px; + border-radius: 4px; + selection-background-color: #2dd4bf; + selection-color: #0f1119; + outline: none; +} + +/* ---- Check Box / Radio Button ---- */ +QCheckBox, +QRadioButton { + spacing: 8px; + color: #d0daf0; +} +QCheckBox::indicator, +QRadioButton::indicator { + border-style: solid; + border-color: #334478; + border-width: 2px; + border-radius: 3px; + background-color: #0a1020; +} +QCheckBox::indicator:hover, +QRadioButton::indicator:hover { + border-color: #2dd4bf; +} +QCheckBox::indicator:checked { + background-color: #2dd4bf; + border-color: #2dd4bf; +} +QRadioButton::indicator { + border-radius: 10px; +} +QRadioButton::indicator:checked { + background-color: #2dd4bf; + border-color: #2dd4bf; +} +QCheckBox::indicator:disabled, +QRadioButton::indicator:disabled { + border-color: #253250; + background-color: #162038; +} + +/* ---- Group Box ---- */ +QGroupBox { + border-style: solid; + border-color: #253250; + border-width: 1px; + border-radius: 6px; + margin-top: 12px; + padding-top: 14px; + color: #d0daf0; + font-weight: bold; +} +QGroupBox::title { + subcontrol-origin: margin; + left: 12px; + padding: 0 6px; + color: #8b9ad0; +} + +/* ---- Tab ---- */ +QTabWidget::pane { + border-style: solid; + border-color: #253250; + border-width: 1px; + border-radius: 5px; + background-color: #0f1a2e; + top: -1px; +} +QTabBar::tab { + background-color: #162038; + border-style: solid; + border-color: #253250; + border-width: 1px; + border-bottom: none; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + padding: 6px 16px; + margin-right: 2px; + color: #7888b8; +} +QTabBar::tab:selected { + background-color: #0f1a2e; + color: #2dd4bf; + border-bottom: 2px solid #2dd4bf; +} +QTabBar::tab:hover:!selected { + background-color: #1c2840; + color: #d0daf0; +} + +/* ---- List / Tree ---- */ +QListWidget, +QTreeWidget, +QTableWidget { + background-color: #0a1020; + border-style: solid; + border-color: #253250; + border-width: 1px; + border-radius: 5px; + outline: none; + color: #d0daf0; + alternate-background-color: #101c30; +} +QListWidget::item, +QTreeWidget::item, +QTableWidget::item { + padding: 5px 10px; + border: none; +} +QListWidget::item:selected, +QTreeWidget::item:selected, +QTableWidget::item:selected { + background-color: #2dd4bf; + color: #0f1119; +} +QListWidget::item:hover:!selected, +QTreeWidget::item:hover:!selected { + background-color: #1c2840; +} +QHeaderView::section { + background-color: #0f1628; + border: none; + border-right: 1px solid #253250; + border-bottom: 1px solid #253250; + padding: 6px 10px; + color: #8b9ad0; + font-weight: bold; +} + +/* ---- Scroll Bar ---- */ +QScrollBar:vertical { + background-color: #0f1a2e; + width: 10px; + border-radius: 5px; +} +QScrollBar::handle:vertical { + background-color: #334478; + min-height: 30px; + border-radius: 5px; +} +QScrollBar::handle:vertical:hover { + background-color: #5568a0; +} +QScrollBar::add-line:vertical, +QScrollBar::sub-line:vertical { + height: 0; +} +QScrollBar:horizontal { + background-color: #0f1a2e; + height: 10px; + border-radius: 5px; +} +QScrollBar::handle:horizontal { + background-color: #334478; + min-width: 30px; + border-radius: 5px; +} +QScrollBar::handle:horizontal:hover { + background-color: #5568a0; +} +QScrollBar::add-line:horizontal, +QScrollBar::sub-line:horizontal { + width: 0; +} + +/* ---- Progress Bar ---- */ +QProgressBar { + background-color: #0a1020; + border-style: solid; + border-color: #253250; + border-width: 1px; + border-radius: 5px; + height: 10px; + text-align: center; + color: #d0daf0; +} +QProgressBar::chunk { + background-color: #2dd4bf; + border-radius: 4px; +} + +/* ---- Slider ---- */ +QSlider::groove:horizontal { + background-color: #1c2840; + height: 6px; + border-radius: 3px; +} +QSlider::handle:horizontal { + background-color: #2dd4bf; + width: 16px; + height: 16px; + margin: -5px 0; + border-radius: 8px; +} +QSlider::sub-page:horizontal { + background-color: #2dd4bf; + border-radius: 3px; +} + +/* ---- Tool Tip ---- */ +QToolTip { + background-color: #1c2840; + border-style: solid; + border-color: #2dd4bf; + border-width: 1px; + border-radius: 4px; + padding: 4px 8px; + color: #d0daf0; +} + +/* ---- Status Bar ---- */ +QStatusBar { + background-color: #0f1628; + border-top: 1px solid #1c2840; + color: #7888b8; +} + +/* ---- Splitter ---- */ +QSplitter::handle { + background-color: #253250; + margin: 1px; +} +QSplitter::handle:horizontal { + width: 2px; +} +QSplitter::handle:vertical { + height: 2px; +} + +/* ---- Dialog ---- */ +QDialog { + background-color: #0f1a2e; +} + +/* ---- Date / Time Editor Drop-down ---- */ +QDateEdit::drop-down, +QTimeEdit::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 24px; + border-left: 1px solid #253250; +} +QCalendarWidget { + background-color: #162038; + border-style: solid; + border-color: #253250; + border-width: 1px; + border-radius: 6px; +} +QCalendarWidget QToolButton { + color: #d0daf0; + border-radius: 4px; + padding: 4px 8px; +} +QCalendarWidget QToolButton:hover { + background-color: #1c2840; +} +QCalendarWidget QMenu { + background-color: #162038; +} + +/* ---- Frame ---- */ +QFrame[frameShape="4"], /* HLine */ +QFrame[frameShape="5"] /* VLine */ { + background-color: #253250; +} diff --git a/src/gui/resources/ui/ALTimerTaskAddDialog.ui b/src/gui/resources/ui/ALTimerTaskAddDialog.ui index e2fc6c4..251a4e2 100644 --- a/src/gui/resources/ui/ALTimerTaskAddDialog.ui +++ b/src/gui/resources/ui/ALTimerTaskAddDialog.ui @@ -7,13 +7,13 @@ 0 0 350 - 400 + 500 350 - 460 + 500