diff --git a/src/gui/ALSettingsWidget.py b/src/gui/ALSettingsWidget.py
index f7f6666..b32aca5 100644
--- a/src/gui/ALSettingsWidget.py
+++ b/src/gui/ALSettingsWidget.py
@@ -139,6 +139,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
):
self.BrowseQssButton.clicked.connect(self.onImportThemeButtonClicked)
+ self.RemoveThemeButton.clicked.connect(self.onRemoveThemeButtonClicked)
self.ThemeComboBox.currentIndexChanged.connect(self.onThemeComboBoxChanged)
self.ResetThemeButton.clicked.connect(self.onResetThemeButtonClicked)
self.CancelButton.clicked.connect(self.onCancelButtonClicked)
@@ -225,7 +226,7 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
t = self.__theme_cache.get(file)
if t:
name = t.get("name", "未知")
- author = t.get("author", "未知")
+ author = t.get("author", "未知作者")
need_theme = t.get("need_theme", "both")
brief = t.get("brief", "没有相关简介")
self.ThemeInfoLabel.setText(
@@ -318,13 +319,46 @@ class ALSettingsWidget(QWidget, Ui_ALSettingsWidget):
author = t.get("author", "")
if name:
self.__theme_cache[file] = t
- if author and author != "未知":
- display = f"{name} ({author})"
- else:
- display = name
- self.ThemeComboBox.addItem(display, file)
+ self.ThemeComboBox.addItem(name, file)
self.ThemeComboBox.blockSignals(False)
+ @Slot()
+ def onRemoveThemeButtonClicked(
+ self
+ ):
+
+ file = self.ThemeComboBox.currentData()
+ if not file:
+ QMessageBox.information(
+ self,
+ "提示 - AutoLibrary",
+ "请先选择一个主题。"
+ )
+ return
+ t = self.__theme_cache.get(file)
+ name = t.get("name", file) if t else file
+ reply = QMessageBox.question(
+ self,
+ "删除主题 - AutoLibrary",
+ f"确定要删除主题 \"{name}\" 吗?",
+ QMessageBox.Yes | QMessageBox.No,
+ QMessageBox.No
+ )
+ if reply != QMessageBox.Yes:
+ return
+ try:
+ themeInstance().removeTheme(file)
+ self.populateThemeList()
+ self.ThemeComboBox.setCurrentIndex(0)
+ self.updateThemeStatus()
+ self.updateThemeInfo()
+ except Exception as e:
+ QMessageBox.warning(
+ self,
+ "删除失败 - AutoLibrary",
+ f"无法删除主题:{e}"
+ )
+
@Slot()
def onImportThemeButtonClicked(
self
diff --git a/src/gui/resources/ui/ALSettingsWidget.ui b/src/gui/resources/ui/ALSettingsWidget.ui
index a661738..413f8f6 100644
--- a/src/gui/resources/ui/ALSettingsWidget.ui
+++ b/src/gui/resources/ui/ALSettingsWidget.ui
@@ -328,7 +328,26 @@
- ...
+ +
+
+
+
+ -
+
+
+
+ 25
+ 25
+
+
+
+
+ 25
+ 25
+
+
+
+ -
diff --git a/src/managers/theme/ThemeManager.py b/src/managers/theme/ThemeManager.py
index 12d55f0..12b0e81 100644
--- a/src/managers/theme/ThemeManager.py
+++ b/src/managers/theme/ThemeManager.py
@@ -121,7 +121,7 @@ class ThemeManager:
existing_info = validateTheme(default_path)
existing_author = existing_info.get("author", "")
except Exception:
- self.removeTheme(theme_name)
+ self._removeThemeFile(theme_name) # caller holds the lock
raise ValueError(
f"主题 '{theme_name}' 已存在但无法通过验证, 已清理该主题文件"
)
@@ -129,7 +129,7 @@ class ThemeManager:
raise ValueError(
f"主题名称 '{theme_name}' (作者 '{author}') 已存在"
)
- safe_author = os.path.basename(author) if author else "未知"
+ safe_author = os.path.basename(author) if author else "未知作者"
alt_path = os.path.join(
self.__themes_dir, f"{theme_name}_{safe_author}.altheme"
)
@@ -168,7 +168,7 @@ class ThemeManager:
with self.__lock:
if ext == ".qss":
name = os.path.splitext(os.path.basename(source_path))[0]
- dest_path = self._resolveDestPath(name, "未知")
+ dest_path = self._resolveDestPath(name, "未知作者")
wrapQssToAtheme(source_path, dest_path, "both")
return os.path.splitext(os.path.basename(dest_path))[0]
elif ext == ".altheme":
@@ -203,7 +203,7 @@ class ThemeManager:
if filename.endswith(".altheme"):
filepath = os.path.join(self.__themes_dir, filename)
try:
- info = validateTheme(filepath)
+ info = validateTheme(filepath, check_qss=False) # skip QSS read for list scan
name = info.get("name", "")
author = info.get("author", "")
key = (name, author)
@@ -225,6 +225,26 @@ class ThemeManager:
)
return themes
+ def _removeThemeFile(
+ self,
+ name: str
+ ):
+ """
+ Remove a theme file without locking.
+
+ The caller must hold self.__lock before invoking this method.
+ """
+
+ filepath = os.path.join(self.__themes_dir, name + ".altheme")
+ if os.path.isfile(filepath):
+ os.remove(filepath)
+ if self.__current_theme_name == name:
+ self.__current_theme_name = ""
+ saved_theme = configInstance().get(
+ CfgKey.GLOBAL.APPEARANCE.THEME, "system"
+ )
+ self.clearTheme(saved_theme)
+
def removeTheme(
self,
name: str
@@ -239,16 +259,8 @@ class ThemeManager:
name (str): The theme name to remove.
"""
- filepath = os.path.join(self.__themes_dir, name + ".altheme")
with self.__lock:
- if os.path.isfile(filepath):
- os.remove(filepath)
- if self.__current_theme_name == name:
- self.__current_theme_name = ""
- saved_theme = configInstance().get(
- CfgKey.GLOBAL.APPEARANCE.THEME, "system"
- )
- self.clearTheme(saved_theme)
+ self._removeThemeFile(name)
def applyTheme(
self,
diff --git a/src/utils/ThemeUtils.py b/src/utils/ThemeUtils.py
index c45c5dc..305f201 100644
--- a/src/utils/ThemeUtils.py
+++ b/src/utils/ThemeUtils.py
@@ -117,7 +117,8 @@ def readThemeQss(
return zf.read("theme.qss").decode("utf-8")
def validateTheme(
- altheme_path: str
+ altheme_path: str,
+ check_qss: bool = True
) -> dict:
"""
Validate a .altheme file and return its metadata.
@@ -128,6 +129,8 @@ def validateTheme(
Args:
altheme_path (str): Path to the .altheme file.
+ check_qss (bool): If False, skip theme.qss existence and
+ content checks (for list-only operations).
Returns:
dict: The validated theme metadata dictionary.
@@ -146,14 +149,16 @@ def validateTheme(
if "theme.qss" not in names:
raise ValueError("无效的 .altheme: 缺少 theme.qss")
info_bytes = zf.read("info.json")
- qss_bytes = zf.read("theme.qss")
+ qss_bytes = zf.read("theme.qss") if check_qss else None # skip QSS read when only listing
try:
info = json.loads(info_bytes.decode("utf-8"))
except (json.JSONDecodeError, UnicodeDecodeError) as e:
raise ValueError(f"无效的 .altheme: info.json 解析失败 — {e}")
if "name" not in info or not isinstance(info.get("name"), str) or not info["name"].strip():
raise ValueError("无效的 .altheme: info.json 缺少有效的 'name' 字段")
- if "author" not in info or not isinstance(info.get("author"), str):
+ # reject blank author so info.json does not drift from the "未知作者" filename fallback
+ if ("author" not in info or not isinstance(info.get("author"), str)
+ or not info["author"].strip()):
raise ValueError("无效的 .altheme: info.json 缺少有效的 'author' 字段")
need_theme = info.get("need_theme", "both")
if need_theme not in ("light", "dark", "both"):
@@ -163,7 +168,7 @@ def validateTheme(
)
if "brief" not in info or not isinstance(info.get("brief"), str):
raise ValueError("无效的 .altheme: info.json 缺少有效的 'brief' 字段")
- if not qss_bytes.strip():
+ if check_qss and not qss_bytes.strip():
raise ValueError("无效的 .altheme: theme.qss 为空")
return info
@@ -191,7 +196,7 @@ def wrapQssToAtheme(
filename = os.path.splitext(os.path.basename(qss_path))[0]
info = {
"name": filename,
- "author": "未知",
+ "author": "未知作者",
"need_theme": current_theme,
"brief": "没有相关简介"
}