1
1
mirror of https://github.com/KenanZhu/AutoLibrary.git synced 2026-06-18 07:23:03 +08:00

feat(theme): 新增 BlueForest 官方深色主题样式

- 新增 BlueForest.qss,基于 Fusion 控件规格的纯配色深色主题
- 深蓝底色 + 亮青绿强调色,控件尺寸与 Fusion 风格保持一致
- 全局统一 selection-background-color 为 #2dd4bf
- 背景色分层:页面 > 头部栏 > 交互控件 > 弹出层 > 输入区
- Border 属性统一拆分为 style/color/width 三段式
- AppInitializer / ALSettingsWidget 配合主题加载

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-05-30 19:27:10 +08:00
parent 05b93799d4
commit 9c1772b186
4 changed files with 468 additions and 21 deletions
+8 -1
View File
@@ -12,7 +12,11 @@ import os
from PySide6.QtCore import QStandardPaths, QDir from PySide6.QtCore import QStandardPaths, QDir
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QApplication
from gui.ALSettingsWidget import _applyTheme from gui.ALSettingsWidget import (
_setActiveStyleName,
_applyTheme,
_applyQss,
)
from interfaces.ConfigProvider import CfgKey from interfaces.ConfigProvider import CfgKey
from managers.config.ConfigManager import instance as configInstance from managers.config.ConfigManager import instance as configInstance
from managers.driver.WebDriverManager import instance as webdriverInstance from managers.driver.WebDriverManager import instance as webdriverInstance
@@ -76,7 +80,10 @@ def _initializeAppearance(
cfg = configInstance() cfg = configInstance()
saved_style = cfg.get(CfgKey.GLOBAL.APPEARANCE.STYLE, "Fusion") saved_style = cfg.get(CfgKey.GLOBAL.APPEARANCE.STYLE, "Fusion")
saved_theme = cfg.get(CfgKey.GLOBAL.APPEARANCE.THEME, "system") saved_theme = cfg.get(CfgKey.GLOBAL.APPEARANCE.THEME, "system")
saved_qss = cfg.get(CfgKey.GLOBAL.APPEARANCE.CUSTOM_QSS, "")
app.setStyle(saved_style) app.setStyle(saved_style)
_setActiveStyleName(saved_style)
_applyQss(saved_qss)
_applyTheme(saved_theme) _applyTheme(saved_theme)
def initializeApp( def initializeApp(
+31 -18
View File
@@ -26,7 +26,6 @@ from PySide6.QtWidgets import (
QApplication, QApplication,
QFileDialog, QFileDialog,
QMessageBox, QMessageBox,
QStyle,
QStyleFactory, QStyleFactory,
QWidget 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( def _clearQss(
): ):
@@ -76,6 +85,7 @@ def _applyTheme(
theme: str theme: str
): ):
global _active_style_name
app : QApplication | None = QApplication.instance() app : QApplication | None = QApplication.instance()
if not app: if not app:
return return
@@ -85,7 +95,7 @@ def _applyTheme(
app.styleHints().setColorScheme(Qt.ColorScheme.Light) app.styleHints().setColorScheme(Qt.ColorScheme.Light)
else: else:
app.styleHints().setColorScheme(Qt.ColorScheme.Unknown) app.styleHints().setColorScheme(Qt.ColorScheme.Unknown)
app.setStyle(QStyleFactory.create(app.style().objectName())) app.setStyle(QStyleFactory.create(_active_style_name))
def _restartApp( def _restartApp(
): ):
@@ -104,7 +114,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
): ):
super().__init__(parent) super().__init__(parent)
self.__cfg_mgr: ConfigProvider = ConfigManager.instance() self.__cfg_mgr: ConfigProvider = ConfigManager.instance()
self.__original_style: QStyle | None = None self.__original_style: str = ""
self.setupUi(self) self.setupUi(self)
self.modifyUi() self.modifyUi()
@@ -137,6 +147,12 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
self.StyleComboBox.clear() self.StyleComboBox.clear()
self.StyleComboBox.addItems(QStyleFactory.keys()) self.StyleComboBox.addItems(QStyleFactory.keys())
def currentStyleKey(
self
) -> str:
return _active_style_name
def connectSignals( def connectSignals(
self self
): ):
@@ -184,7 +200,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
theme = self.__cfg_mgr.get(CfgKey.GLOBAL.APPEARANCE.THEME, "system") theme = self.__cfg_mgr.get(CfgKey.GLOBAL.APPEARANCE.THEME, "system")
style = self.__cfg_mgr.get(CfgKey.GLOBAL.APPEARANCE.STYLE, "Fusion") style = self.__cfg_mgr.get(CfgKey.GLOBAL.APPEARANCE.STYLE, "Fusion")
custom_qss = self.__cfg_mgr.get(CfgKey.GLOBAL.APPEARANCE.CUSTOM_QSS, "") 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": if theme == "light":
self.LightThemeRadio.setChecked(True) self.LightThemeRadio.setChecked(True)
elif theme == "dark": elif theme == "dark":
@@ -192,10 +208,9 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
else: else:
self.SystemThemeRadio.setChecked(True) self.SystemThemeRadio.setChecked(True)
index = self.StyleComboBox.findText(style) index = self.StyleComboBox.findText(style)
if index >= 0: if index < 0:
self.StyleComboBox.setCurrentIndex(index) index = 0
else: self.StyleComboBox.setCurrentIndex(index)
self.StyleComboBox.setCurrentIndex(0)
self.QssPathEdit.setText(custom_qss) self.QssPathEdit.setText(custom_qss)
self.updateQssStatus(custom_qss) self.updateQssStatus(custom_qss)
@@ -205,7 +220,8 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
): ):
if qss_path and os.path.isfile(qss_path): 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: else:
self.QssStatusLabel.setText("当前使用程序默认外观。") self.QssStatusLabel.setText("当前使用程序默认外观。")
@@ -219,7 +235,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
theme = "dark" theme = "dark"
else: else:
theme = "system" theme = "system"
style = QStyleFactory.create(self.StyleComboBox.currentText()) style = self.StyleComboBox.currentText()
custom_qss = self.QssPathEdit.text().strip() custom_qss = self.QssPathEdit.text().strip()
return theme, style, custom_qss return theme, style, custom_qss
@@ -229,16 +245,13 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
theme, style, custom_qss = self.collectSettings() theme, style, custom_qss = self.collectSettings()
self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.THEME, theme) 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) self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.CUSTOM_QSS, custom_qss)
if custom_qss and os.path.isfile(custom_qss): _applyQss(custom_qss)
_applyQss(custom_qss)
else:
_clearQss()
_applyTheme(theme) _applyTheme(theme)
self.setNavigationIcons() self.setNavigationIcons()
self.updateQssStatus(custom_qss) self.updateQssStatus(custom_qss)
self.__original_style = QApplication.instance().style() self.__original_style = self.currentStyleKey()
def maybeRestart( def maybeRestart(
self self
@@ -322,7 +335,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
): ):
_, style, _ = self.collectSettings() _, style, _ = self.collectSettings()
style_changed = self.__original_style.name() != style.name() style_changed = self.__original_style != style
self.saveAndApply() self.saveAndApply()
if style_changed: if style_changed:
self.maybeRestart() self.maybeRestart()
@@ -333,7 +346,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
): ):
_, style, _ = self.collectSettings() _, style, _ = self.collectSettings()
style_changed = self.__original_style.name() != style.name() style_changed = self.__original_style != style
self.saveAndApply() self.saveAndApply()
if style_changed: if style_changed:
self.maybeRestart() self.maybeRestart()
+427
View File
@@ -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;
}
+2 -2
View File
@@ -7,13 +7,13 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>350</width> <width>350</width>
<height>400</height> <height>500</height>
</rect> </rect>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>350</width> <width>350</width>
<height>460</height> <height>500</height>
</size> </size>
</property> </property>
<property name="maximumSize"> <property name="maximumSize">