mirror of
https://github.com/KenanZhu/AutoLibrary.git
synced 2026-06-18 07:23:03 +08:00
refactor(theme): 将重复的主题逻辑下沉至 ThemeManager
ThemeManager 新增: - clearTheme(theme) — 清除 QSS 并应用指定色调 - applyThemeOrClear(name, fallback) — 应用或回退的封装 - _applyColorScheme(theme) — Qt ColorScheme 设置的统一入口 - themeToReadable(need_theme) — 静态工具方法 ALSettingsWidget 移除: - _clearCustomTheme → 改用 themeInstance().clearTheme() - _applyCustomTheme → 改用 themeInstance().applyThemeOrClear() - _themeToReadable → 改用 ThemeManager.themeToReadable() ALSettingsWidget 仅保留 _applyTheme(含 setStyle 逻辑,供 AppInitializer 使用)
This commit is contained in:
@@ -20,6 +20,7 @@ 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
|
||||||
from managers.log.LogManager import instance as logInstance
|
from managers.log.LogManager import instance as logInstance
|
||||||
|
from managers.theme.ThemeManager import instance as themeInstance
|
||||||
|
|
||||||
|
|
||||||
def _initializeLogManager(
|
def _initializeLogManager(
|
||||||
@@ -82,12 +83,12 @@ def _initializeAppearance(
|
|||||||
saved_custom_theme = cfg.get(CfgKey.GLOBAL.APPEARANCE.CUSTOM_THEME, "")
|
saved_custom_theme = cfg.get(CfgKey.GLOBAL.APPEARANCE.CUSTOM_THEME, "")
|
||||||
app.setStyle(saved_style)
|
app.setStyle(saved_style)
|
||||||
_setActiveStyleName(saved_style)
|
_setActiveStyleName(saved_style)
|
||||||
|
logger = logInstance().getLogger("AppInitializer")
|
||||||
if saved_custom_theme:
|
if saved_custom_theme:
|
||||||
try:
|
try:
|
||||||
from managers.theme.ThemeManager import instance as themeInstance
|
|
||||||
themeInstance().applyTheme(saved_custom_theme)
|
themeInstance().applyTheme(saved_custom_theme)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
logger.warning("无法应用自定义主题 '%s',回退到默认外观", saved_custom_theme)
|
||||||
_applyTheme(saved_theme)
|
_applyTheme(saved_theme)
|
||||||
|
|
||||||
def initializeApp(
|
def initializeApp(
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ from PySide6.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
import managers.config.ConfigManager as ConfigManager
|
import managers.config.ConfigManager as ConfigManager
|
||||||
from managers.theme.ThemeManager import instance as themeInstance
|
from managers.theme.ThemeManager import (
|
||||||
|
ThemeManager,
|
||||||
|
instance as themeInstance
|
||||||
|
)
|
||||||
|
|
||||||
from gui.resources.ui.Ui_ALSettingsWidget import Ui_ALSettingsWidget
|
from gui.resources.ui.Ui_ALSettingsWidget import Ui_ALSettingsWidget
|
||||||
from interfaces.ConfigProvider import (
|
from interfaces.ConfigProvider import (
|
||||||
@@ -50,28 +53,6 @@ def _setActiveStyleName(
|
|||||||
global _active_style_name
|
global _active_style_name
|
||||||
_active_style_name = name
|
_active_style_name = name
|
||||||
|
|
||||||
def _clearCustomTheme(
|
|
||||||
theme: str
|
|
||||||
):
|
|
||||||
|
|
||||||
app : QApplication | None = QApplication.instance()
|
|
||||||
if app:
|
|
||||||
app.setStyleSheet("")
|
|
||||||
_applyTheme(theme)
|
|
||||||
|
|
||||||
def _applyCustomTheme(
|
|
||||||
name: str,
|
|
||||||
fallback_theme: str = "system"
|
|
||||||
):
|
|
||||||
|
|
||||||
if not name:
|
|
||||||
_clearCustomTheme(fallback_theme)
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
themeInstance().applyTheme(name)
|
|
||||||
except Exception:
|
|
||||||
_clearCustomTheme(fallback_theme)
|
|
||||||
|
|
||||||
def _applyTheme(
|
def _applyTheme(
|
||||||
theme: str
|
theme: str
|
||||||
):
|
):
|
||||||
@@ -94,19 +75,6 @@ def _restartApp(
|
|||||||
QApplication.instance().quit()
|
QApplication.instance().quit()
|
||||||
QProcess.startDetached(sys.executable, sys.argv)
|
QProcess.startDetached(sys.executable, sys.argv)
|
||||||
|
|
||||||
def _themeToReadable(
|
|
||||||
theme: str
|
|
||||||
) -> str:
|
|
||||||
|
|
||||||
if theme == "dark":
|
|
||||||
return "深色"
|
|
||||||
elif theme == "light":
|
|
||||||
return "浅色"
|
|
||||||
elif theme == "both":
|
|
||||||
return "所有"
|
|
||||||
else:
|
|
||||||
return "未知"
|
|
||||||
|
|
||||||
class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
|
class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
|
||||||
|
|
||||||
settingsWidgetIsClosed = Signal()
|
settingsWidgetIsClosed = Signal()
|
||||||
@@ -136,8 +104,8 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
|
|||||||
self.setNavigationIcons()
|
self.setNavigationIcons()
|
||||||
self.ThemeInfoLabel.setTextFormat(Qt.TextFormat.RichText)
|
self.ThemeInfoLabel.setTextFormat(Qt.TextFormat.RichText)
|
||||||
self.ThemeInfoLabel.setStyleSheet(
|
self.ThemeInfoLabel.setStyleSheet(
|
||||||
"border: 1px solid #ccc; " \
|
"border: 1px solid palette(mid);"\
|
||||||
"border-radius: 2px;" \
|
"border-radius: 2px;"\
|
||||||
"padding: 5px;"
|
"padding: 5px;"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -256,7 +224,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
|
|||||||
need_theme = t.get("need_theme", "both")
|
need_theme = t.get("need_theme", "both")
|
||||||
brief = t.get("brief", "没有相关简介")
|
brief = t.get("brief", "没有相关简介")
|
||||||
self.ThemeInfoLabel.setText(
|
self.ThemeInfoLabel.setText(
|
||||||
f"<b>{name}</b> - 适用于 <i>{_themeToReadable(need_theme)}</i> 主题<br>"
|
f"<b>{name}</b> - 适用于 <i>{ThemeManager.themeToReadable(need_theme)}</i> 主题<br>"
|
||||||
f"作者:{author}<br><br>"
|
f"作者:{author}<br><br>"
|
||||||
f"{brief}"
|
f"{brief}"
|
||||||
)
|
)
|
||||||
@@ -299,7 +267,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
|
|||||||
theme, style, custom_theme = self.collectSettings()
|
theme, style, custom_theme = self.collectSettings()
|
||||||
self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.STYLE, style)
|
self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.STYLE, style)
|
||||||
self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.CUSTOM_THEME, custom_theme)
|
self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.CUSTOM_THEME, custom_theme)
|
||||||
_applyCustomTheme(custom_theme, theme)
|
themeInstance().applyThemeOrClear(custom_theme, theme)
|
||||||
self.syncRadioFromNeedTheme(custom_theme)
|
self.syncRadioFromNeedTheme(custom_theme)
|
||||||
theme, _, _ = self.collectSettings()
|
theme, _, _ = self.collectSettings()
|
||||||
self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.THEME, theme)
|
self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.THEME, theme)
|
||||||
@@ -391,6 +359,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
|
|||||||
self.DarkThemeRadio.setChecked(True)
|
self.DarkThemeRadio.setChecked(True)
|
||||||
else:
|
else:
|
||||||
self.SystemThemeRadio.setChecked(True)
|
self.SystemThemeRadio.setChecked(True)
|
||||||
|
themeInstance().clearTheme(self.__original_theme)
|
||||||
self.updateThemeInfo()
|
self.updateThemeInfo()
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ from managers.config.ConfigManager import instance as configInstance
|
|||||||
from utils.ThemeUtils import (
|
from utils.ThemeUtils import (
|
||||||
packTheme,
|
packTheme,
|
||||||
readThemeInfo,
|
readThemeInfo,
|
||||||
unpackTheme
|
unpackTheme,
|
||||||
|
wrapQssToAtheme
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -85,14 +86,10 @@ class ThemeManager:
|
|||||||
ext = os.path.splitext(source_path)[1].lower()
|
ext = os.path.splitext(source_path)[1].lower()
|
||||||
if ext == ".qss":
|
if ext == ".qss":
|
||||||
name = os.path.splitext(os.path.basename(source_path))[0]
|
name = os.path.splitext(os.path.basename(source_path))[0]
|
||||||
info = {
|
|
||||||
"name": name,
|
|
||||||
"author": "未知",
|
|
||||||
"need_theme": "both",
|
|
||||||
"brief": "没有相关简介"
|
|
||||||
}
|
|
||||||
dest_path = os.path.join(self.__themes_dir, name + ".altheme")
|
dest_path = os.path.join(self.__themes_dir, name + ".altheme")
|
||||||
packTheme(source_path, info, dest_path)
|
if os.path.exists(dest_path):
|
||||||
|
raise ValueError(f"主题 '{name}' 已存在")
|
||||||
|
wrapQssToAtheme(source_path, dest_path, "both")
|
||||||
return name
|
return name
|
||||||
elif ext == ".altheme":
|
elif ext == ".altheme":
|
||||||
with zipfile.ZipFile(source_path, "r") as zf:
|
with zipfile.ZipFile(source_path, "r") as zf:
|
||||||
@@ -102,6 +99,8 @@ class ThemeManager:
|
|||||||
name = info.get("name", os.path.splitext(os.path.basename(source_path))[0])
|
name = info.get("name", os.path.splitext(os.path.basename(source_path))[0])
|
||||||
safe_name = os.path.basename(name)
|
safe_name = os.path.basename(name)
|
||||||
dest_path = os.path.join(self.__themes_dir, safe_name + ".altheme")
|
dest_path = os.path.join(self.__themes_dir, safe_name + ".altheme")
|
||||||
|
if os.path.exists(dest_path):
|
||||||
|
raise ValueError(f"主题 '{safe_name}' 已存在")
|
||||||
shutil.copy2(source_path, dest_path)
|
shutil.copy2(source_path, dest_path)
|
||||||
return safe_name
|
return safe_name
|
||||||
else:
|
else:
|
||||||
@@ -176,25 +175,108 @@ class ThemeManager:
|
|||||||
filepath = os.path.join(self.__themes_dir, name + ".altheme")
|
filepath = os.path.join(self.__themes_dir, name + ".altheme")
|
||||||
if not os.path.isfile(filepath):
|
if not os.path.isfile(filepath):
|
||||||
raise FileNotFoundError(filepath)
|
raise FileNotFoundError(filepath)
|
||||||
info = readThemeInfo(filepath)
|
with self.__lock:
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
info = readThemeInfo(filepath)
|
||||||
unpackTheme(filepath, tmpdir)
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
qss_path = os.path.join(tmpdir, "theme.qss")
|
unpackTheme(filepath, tmpdir)
|
||||||
if os.path.isfile(qss_path):
|
qss_path = os.path.join(tmpdir, "theme.qss")
|
||||||
with open(qss_path, "r", encoding="utf-8") as fh:
|
if os.path.isfile(qss_path):
|
||||||
qss = fh.read()
|
with open(qss_path, "r", encoding="utf-8") as fh:
|
||||||
app = QApplication.instance()
|
qss = fh.read()
|
||||||
if app:
|
app = QApplication.instance()
|
||||||
app.setStyleSheet(qss)
|
if app:
|
||||||
|
app.setStyleSheet(qss)
|
||||||
|
app = QApplication.instance()
|
||||||
|
if app:
|
||||||
|
need_theme = info.get("need_theme", "both")
|
||||||
|
if need_theme == "dark":
|
||||||
|
app.styleHints().setColorScheme(Qt.ColorScheme.Dark)
|
||||||
|
elif need_theme == "light":
|
||||||
|
app.styleHints().setColorScheme(Qt.ColorScheme.Light)
|
||||||
|
self.__current_theme_name = name
|
||||||
|
|
||||||
|
def clearTheme(
|
||||||
|
self,
|
||||||
|
theme: str
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Clear the current QSS stylesheet and apply the given color scheme.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
theme (str): The color scheme to apply after clearing
|
||||||
|
("light", "dark", or "system").
|
||||||
|
"""
|
||||||
|
|
||||||
app = QApplication.instance()
|
app = QApplication.instance()
|
||||||
if app:
|
if app:
|
||||||
need_theme = info.get("need_theme", "both")
|
app.setStyleSheet("")
|
||||||
if need_theme == "dark":
|
self._applyColorScheme(theme)
|
||||||
app.styleHints().setColorScheme(Qt.ColorScheme.Dark)
|
|
||||||
elif need_theme == "light":
|
def applyThemeOrClear(
|
||||||
app.styleHints().setColorScheme(Qt.ColorScheme.Light)
|
self,
|
||||||
with self.__lock:
|
name: str,
|
||||||
self.__current_theme_name = name
|
fallback_theme: str = "system"
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Apply a custom theme by name, or clear to fallback if empty.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The theme name to apply, or empty to clear.
|
||||||
|
fallback_theme (str): Color scheme to use if name is empty
|
||||||
|
or if the theme fails to apply.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not name:
|
||||||
|
self.clearTheme(fallback_theme)
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
self.applyTheme(name)
|
||||||
|
except Exception:
|
||||||
|
self.clearTheme(fallback_theme)
|
||||||
|
|
||||||
|
def _applyColorScheme(
|
||||||
|
self,
|
||||||
|
theme: str
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Set the Qt application color scheme.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
theme (str): "dark", "light", or any other value for system default.
|
||||||
|
"""
|
||||||
|
|
||||||
|
app = QApplication.instance()
|
||||||
|
if not app:
|
||||||
|
return
|
||||||
|
if theme == "dark":
|
||||||
|
app.styleHints().setColorScheme(Qt.ColorScheme.Dark)
|
||||||
|
elif theme == "light":
|
||||||
|
app.styleHints().setColorScheme(Qt.ColorScheme.Light)
|
||||||
|
else:
|
||||||
|
app.styleHints().setColorScheme(Qt.ColorScheme.Unknown)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def themeToReadable(
|
||||||
|
need_theme: str
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Convert a need_theme code to human-readable Chinese text.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
need_theme (str): "dark", "light", "both", or other.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Readable Chinese label.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if need_theme == "dark":
|
||||||
|
return "深色"
|
||||||
|
elif need_theme == "light":
|
||||||
|
return "浅色"
|
||||||
|
elif need_theme == "both":
|
||||||
|
return "所有"
|
||||||
|
else:
|
||||||
|
return "未知"
|
||||||
|
|
||||||
def currentThemeName(
|
def currentThemeName(
|
||||||
self
|
self
|
||||||
|
|||||||
@@ -89,7 +89,10 @@ def readThemeInfo(
|
|||||||
if "info.json" not in zf.namelist():
|
if "info.json" not in zf.namelist():
|
||||||
raise ValueError("无效的 .altheme: 缺少 info.json")
|
raise ValueError("无效的 .altheme: 缺少 info.json")
|
||||||
with zf.open("info.json") as fh:
|
with zf.open("info.json") as fh:
|
||||||
return json.loads(fh.read().decode("utf-8"))
|
info = json.loads(fh.read().decode("utf-8"))
|
||||||
|
if "name" not in info:
|
||||||
|
raise ValueError("无效的 .altheme: info.json 缺少 'name' 字段")
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
def wrapQssToAtheme(
|
def wrapQssToAtheme(
|
||||||
|
|||||||
Reference in New Issue
Block a user