This commit is contained in:
2025-11-23 20:41:50 +08:00
commit f7d5b7be07
65 changed files with 14986 additions and 0 deletions

View File

@@ -0,0 +1,202 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
图标管理器
负责图标的加载、缓存和管理
"""
import os
import glob
import customtkinter as ctk
from PIL import Image
from functools import lru_cache
from collections import OrderedDict
from config.constants import APP_ICON_MAPPING
class IconManager:
"""图标管理器,负责图标的加载、缓存和管理"""
def __init__(self, icons_dir: str, icon_size: int):
"""
初始化图标管理器
Args:
icons_dir: 图标目录路径
icon_size: 图标大小
"""
self.icons_dir = icons_dir
self.icon_size = icon_size
# 使用有界缓存控制内存占用
self._max_cache_size = 128
self.cache: OrderedDict[str, ctk.CTkImage] = OrderedDict()
def get_app_icon(self, app_path: str, config_manager) -> ctk.CTkImage:
"""
获取应用图标
Args:
app_path: 应用路径
config_manager: 配置管理器
Returns:
CTkImage 对象,如果失败则返回 None
"""
# 检查缓存
if app_path in self.cache:
self.cache.move_to_end(app_path)
return self.cache[app_path]
try:
# 查找图标路径
icon_path = self._find_icon_path(app_path, config_manager)
if not icon_path:
return self._get_fallback_icon(app_path)
# 创建并缓存图标
ctk_image = self._create_ctk_icon(icon_path)
self.cache[app_path] = ctk_image
# 控制缓存大小
if len(self.cache) > self._max_cache_size:
self.cache.popitem(last=False)
return ctk_image
except Exception as e:
print(f"Failed to load icon ({app_path}): {e}")
return self._get_fallback_icon(app_path)
def _find_icon_path(self, app_path: str, config_manager) -> str:
"""
查找应用图标路径
查找优先级:
1. 自定义图标
2. 预设图标映射
3. 应用名称匹配
4. 默认图标
5. 任意可用图标
Args:
app_path: 应用路径
config_manager: 配置管理器
Returns:
图标文件路径,如果未找到则返回 None
"""
app_name = os.path.splitext(os.path.basename(app_path))[0]
# 1. 检查自定义图标
custom_icon = config_manager.get_app_icon(app_path)
if custom_icon and os.path.exists(custom_icon):
return custom_icon
# 2. 匹配预设图标
app_name_lower = app_name.lower()
for key, icon_name in APP_ICON_MAPPING.items():
if key in app_name_lower:
icon_path = os.path.join(self.icons_dir, f"{icon_name}.png")
if os.path.exists(icon_path):
return icon_path
# 3. 使用应用名称
icon_path = os.path.join(self.icons_dir, f"{app_name}.png")
if os.path.exists(icon_path):
return icon_path
# 4. 使用默认图标
default_icon = os.path.join(self.icons_dir, "NexusLauncher.ico")
if os.path.exists(default_icon):
return default_icon
# 5. 使用任意可用图标
icons = glob.glob(os.path.join(self.icons_dir, "*.png"))
icons.extend(glob.glob(os.path.join(self.icons_dir, "*.ico")))
return icons[0] if icons else None
@lru_cache(maxsize=128)
def _load_pil_image(self, icon_path: str) -> Image.Image:
"""
加载 PIL 图像(带 LRU 缓存)
使用 LRU 缓存可以避免重复加载相同的图标文件,
提升性能并减少磁盘 I/O
Args:
icon_path: 图标文件路径
Returns:
PIL Image 对象
"""
return Image.open(icon_path)
def _create_ctk_icon(self, icon_path: str) -> ctk.CTkImage:
"""
创建 CTk 图标对象
Args:
icon_path: 图标文件路径
Returns:
CTkImage 对象
"""
pil_image = self._load_pil_image(icon_path)
icon_display_size = int(self.icon_size * 0.6)
return ctk.CTkImage(
light_image=pil_image,
dark_image=pil_image,
size=(icon_display_size, icon_display_size)
)
def _get_fallback_icon(self, app_path: str) -> ctk.CTkImage:
"""
获取降级图标
当无法加载指定图标时,尝试使用任意可用图标
Args:
app_path: 应用路径
Returns:
CTkImage 对象,如果失败则返回 None
"""
try:
icons = glob.glob(os.path.join(self.icons_dir, "*.png"))
if icons:
ctk_image = self._create_ctk_icon(icons[0])
self.cache[app_path] = ctk_image
if len(self.cache) > self._max_cache_size:
self.cache.popitem(last=False)
return ctk_image
except:
pass
return None
def clear_cache(self):
"""清空所有缓存"""
self.cache.clear()
self._load_pil_image.cache_clear()
def update_icon_size(self, new_size: int):
"""
更新图标大小
更新图标大小后会清空缓存,
下次获取图标时会使用新的尺寸重新创建
Args:
new_size: 新的图标大小
"""
self.icon_size = new_size
self.clear_cache()
def get_cache_info(self) -> dict:
"""
获取缓存信息
Returns:
包含缓存统计信息的字典
"""
return {
'ctk_cache_size': len(self.cache),
'pil_cache_info': self._load_pil_image.cache_info()._asdict()
}