mirror of
https://github.com/KenanZhu/AutoLibrary.git
synced 2026-06-17 23:13:03 +08:00
fix(theme): 修复主题管理系统逻辑缺陷
- removeTheme() 删除当前活动主题后从 ConfigManager 读取已保存的主题偏好作为回退方案,不再硬编码 'system' - saveAndApply() 在调用 _applyCustomTheme 前先 setActiveStyle(style),确保主题应用时使用最新选择的内置样式 - _applyCustomTheme() 返回 bool 表示成败,失败时调用方清除配置中的 custom_theme 避免下次启动循环失败 - importTheme() 增加 self.__lock 保护,消除 TOCTOU 竞态条件 - ThemeManager 新增 CfgKey 导入以支持 removeTheme 读取配置 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,7 @@ import managers.config.ConfigManager as ConfigManager
|
||||
from managers.log.LogManager import instance as logInstance
|
||||
from managers.theme.ThemeManager import(
|
||||
getActiveStyle,
|
||||
setActiveStyle,
|
||||
instance as themeInstance
|
||||
)
|
||||
|
||||
@@ -47,18 +48,20 @@ from interfaces.ConfigProvider import (
|
||||
def _applyCustomTheme(
|
||||
name: str,
|
||||
fallback_theme: str = "system"
|
||||
):
|
||||
) -> bool:
|
||||
|
||||
if not name:
|
||||
themeInstance().clearTheme(fallback_theme)
|
||||
return
|
||||
return True
|
||||
try:
|
||||
themeInstance().applyTheme(name)
|
||||
return True
|
||||
except Exception as e:
|
||||
logInstance().getLogger("ALSettingsWidget").warning(
|
||||
f"无法应用自定义主题 '{name}',回退到 {fallback_theme} 外观: {e}"
|
||||
)
|
||||
themeInstance().clearTheme(fallback_theme)
|
||||
return False
|
||||
|
||||
def _themeToReadable(
|
||||
need_theme: str
|
||||
@@ -266,7 +269,9 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
|
||||
theme, style, custom_theme = self.collectSettings()
|
||||
self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.STYLE, style)
|
||||
self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.CUSTOM_THEME, custom_theme)
|
||||
_applyCustomTheme(custom_theme, theme)
|
||||
setActiveStyle(style)
|
||||
if not _applyCustomTheme(custom_theme, theme):
|
||||
self.__cfg_mgr.set(CfgKey.GLOBAL.APPEARANCE.CUSTOM_THEME, "")
|
||||
self.syncRadioFromNeedTheme(custom_theme)
|
||||
# Re-read theme after syncRadioFromNeedTheme — the radio may have
|
||||
# changed to match the custom theme's need_theme
|
||||
|
||||
@@ -19,6 +19,7 @@ from PySide6.QtWidgets import (
|
||||
QStyleFactory
|
||||
)
|
||||
|
||||
from interfaces.ConfigProvider import CfgKey
|
||||
from managers.config.ConfigManager import instance as configInstance
|
||||
from managers.log.LogManager import instance as logInstance
|
||||
from utils.ThemeUtils import (
|
||||
@@ -119,35 +120,36 @@ class ThemeManager:
|
||||
if not os.path.isfile(source_path):
|
||||
raise FileNotFoundError(source_path)
|
||||
ext = os.path.splitext(source_path)[1].lower()
|
||||
if ext == ".qss":
|
||||
name = os.path.splitext(os.path.basename(source_path))[0]
|
||||
dest_path = os.path.join(self.__themes_dir, name + ".altheme")
|
||||
if os.path.exists(dest_path):
|
||||
raise ValueError(f"主题 '{name}' 已存在")
|
||||
wrapQssToAtheme(source_path, dest_path, "both")
|
||||
return name
|
||||
elif ext == ".altheme":
|
||||
with zipfile.ZipFile(source_path, "r") as zf:
|
||||
if "theme.qss" not in zf.namelist():
|
||||
raise ValueError("无效的 .altheme: 缺少 theme.qss")
|
||||
info = readThemeInfo(source_path)
|
||||
name = info.get("name", os.path.splitext(os.path.basename(source_path))[0])
|
||||
safe_name = os.path.basename(name)
|
||||
dest_path = os.path.join(self.__themes_dir, safe_name + ".altheme")
|
||||
if os.path.exists(dest_path):
|
||||
raise ValueError(f"主题 '{safe_name}' 已存在")
|
||||
# Check for name collision with existing themes by the same author
|
||||
new_author = info.get("author", "")
|
||||
for existing in self.listThemes():
|
||||
if (existing.get("name", "") == safe_name
|
||||
and existing.get("author", "") == new_author):
|
||||
raise ValueError(
|
||||
f"主题名称 '{safe_name}' (作者 '{new_author}') 已存在"
|
||||
)
|
||||
shutil.copy2(source_path, dest_path)
|
||||
return safe_name
|
||||
else:
|
||||
raise ValueError(f"不支持的文件类型: {ext}")
|
||||
with self.__lock:
|
||||
if ext == ".qss":
|
||||
name = os.path.splitext(os.path.basename(source_path))[0]
|
||||
dest_path = os.path.join(self.__themes_dir, name + ".altheme")
|
||||
if os.path.exists(dest_path):
|
||||
raise ValueError(f"主题 '{name}' 已存在")
|
||||
wrapQssToAtheme(source_path, dest_path, "both")
|
||||
return name
|
||||
elif ext == ".altheme":
|
||||
with zipfile.ZipFile(source_path, "r") as zf:
|
||||
if "theme.qss" not in zf.namelist():
|
||||
raise ValueError("无效的 .altheme: 缺少 theme.qss")
|
||||
info = readThemeInfo(source_path)
|
||||
name = info.get("name", os.path.splitext(os.path.basename(source_path))[0])
|
||||
safe_name = os.path.basename(name)
|
||||
dest_path = os.path.join(self.__themes_dir, safe_name + ".altheme")
|
||||
if os.path.exists(dest_path):
|
||||
raise ValueError(f"主题 '{safe_name}' 已存在")
|
||||
# Check for name collision with existing themes by the same author
|
||||
new_author = info.get("author", "")
|
||||
for existing in self.listThemes():
|
||||
if (existing.get("name", "") == safe_name
|
||||
and existing.get("author", "") == new_author):
|
||||
raise ValueError(
|
||||
f"主题名称 '{safe_name}' (作者 '{new_author}') 已存在"
|
||||
)
|
||||
shutil.copy2(source_path, dest_path)
|
||||
return safe_name
|
||||
else:
|
||||
raise ValueError(f"不支持的文件类型: {ext}")
|
||||
|
||||
def listThemes(
|
||||
self
|
||||
@@ -214,7 +216,10 @@ class ThemeManager:
|
||||
os.remove(filepath)
|
||||
if self.__current_theme_name == name:
|
||||
self.__current_theme_name = ""
|
||||
self.clearTheme("system")
|
||||
saved_theme = configInstance().get(
|
||||
CfgKey.GLOBAL.APPEARANCE.THEME, "system"
|
||||
)
|
||||
self.clearTheme(saved_theme)
|
||||
|
||||
def applyTheme(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user