Update
This commit is contained in:
202
ui/utilities/icon_manager.py
Normal file
202
ui/utilities/icon_manager.py
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user