优化GUI程序,加入线程池,避免翻页卡死现象发生。
This commit is contained in:
@@ -1,40 +1,92 @@
|
||||
"""
|
||||
图片加载工具
|
||||
用于从URL加载图片并转换为PIL/CTkImage格式
|
||||
优化版本:使用连接池、线程池和缓存
|
||||
"""
|
||||
import io
|
||||
import requests
|
||||
from PIL import Image
|
||||
from typing import Optional
|
||||
import customtkinter as ctk
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from functools import lru_cache
|
||||
import threading
|
||||
|
||||
|
||||
# ======================== 全局资源管理 ========================
|
||||
|
||||
# HTTP会话(连接池)
|
||||
_session = None
|
||||
_session_lock = threading.Lock()
|
||||
|
||||
def get_session() -> requests.Session:
|
||||
"""获取全局HTTP会话(单例模式)"""
|
||||
global _session
|
||||
if _session is None:
|
||||
with _session_lock:
|
||||
if _session is None:
|
||||
_session = requests.Session()
|
||||
# 配置连接池
|
||||
adapter = requests.adapters.HTTPAdapter(
|
||||
pool_connections=10, # 连接池大小
|
||||
pool_maxsize=20, # 最大连接数
|
||||
max_retries=2, # 重试次数
|
||||
pool_block=False
|
||||
)
|
||||
_session.mount('http://', adapter)
|
||||
_session.mount('https://', adapter)
|
||||
return _session
|
||||
|
||||
|
||||
# 线程池(用于图片加载)
|
||||
_thread_pool = None
|
||||
_thread_pool_lock = threading.Lock()
|
||||
|
||||
def get_thread_pool() -> ThreadPoolExecutor:
|
||||
"""获取全局线程池(单例模式)"""
|
||||
global _thread_pool
|
||||
if _thread_pool is None:
|
||||
with _thread_pool_lock:
|
||||
if _thread_pool is None:
|
||||
# 限制最多同时加载10张图片
|
||||
_thread_pool = ThreadPoolExecutor(
|
||||
max_workers=10,
|
||||
thread_name_prefix="ImageLoader"
|
||||
)
|
||||
return _thread_pool
|
||||
|
||||
|
||||
# ======================== 图片加载函数 ========================
|
||||
|
||||
def load_image_from_url(
|
||||
url: str,
|
||||
size: tuple = (150, 150),
|
||||
timeout: int = 10
|
||||
timeout: int = 5 # 降低超时时间,避免长时间阻塞
|
||||
) -> Optional[ctk.CTkImage]:
|
||||
"""
|
||||
从URL加载图片
|
||||
从URL加载图片(使用连接池)
|
||||
|
||||
Args:
|
||||
url: 图片URL
|
||||
size: 目标尺寸 (width, height)
|
||||
timeout: 超时时间(秒)
|
||||
timeout: 超时时间(秒)
|
||||
|
||||
Returns:
|
||||
CTkImage对象,失败返回None
|
||||
CTkImage对象,失败返回None
|
||||
"""
|
||||
try:
|
||||
# 使用全局session(连接复用)
|
||||
session = get_session()
|
||||
|
||||
# 下载图片
|
||||
response = requests.get(url, timeout=timeout)
|
||||
response = session.get(url, timeout=timeout)
|
||||
response.raise_for_status()
|
||||
|
||||
# 转换为PIL Image
|
||||
image_data = io.BytesIO(response.content)
|
||||
pil_image = Image.open(image_data)
|
||||
|
||||
# 转换为RGB(防止RGBA等格式问题)
|
||||
# 转换为RGB(防止RGBA等格式问题)
|
||||
if pil_image.mode != 'RGB':
|
||||
pil_image = pil_image.convert('RGB')
|
||||
|
||||
@@ -47,17 +99,24 @@ def load_image_from_url(
|
||||
|
||||
return ctk_image
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
print(f"加载图片超时: {url}")
|
||||
return None
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"网络请求失败 {url}: {e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"加载图片失败 {url}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
@lru_cache(maxsize=100)
|
||||
def create_placeholder_image(
|
||||
size: tuple = (150, 150),
|
||||
text: str = "加载失败"
|
||||
) -> ctk.CTkImage:
|
||||
"""
|
||||
创建占位图片
|
||||
创建占位图片(带缓存)
|
||||
|
||||
Args:
|
||||
size: 图片尺寸
|
||||
@@ -76,3 +135,18 @@ def create_placeholder_image(
|
||||
)
|
||||
|
||||
return ctk_image
|
||||
|
||||
|
||||
def cleanup_resources():
|
||||
"""清理全局资源(程序退出时调用)"""
|
||||
global _session, _thread_pool
|
||||
|
||||
# 关闭线程池
|
||||
if _thread_pool is not None:
|
||||
_thread_pool.shutdown(wait=False)
|
||||
_thread_pool = None
|
||||
|
||||
# 关闭HTTP会话
|
||||
if _session is not None:
|
||||
_session.close()
|
||||
_session = None
|
||||
|
||||
Reference in New Issue
Block a user