refactor: implement UI code optimization and refactoring
This commit implements the comprehensive UI code refactoring plan to improve code quality, reduce duplication, and enhance maintainability. Phase 1: High Priority Improvements - Create BaseDialog class to eliminate window centering code duplication across ProgressDialog, DeleteProgressWindow, LoginDialog, and UserSelectionDialog (~30 lines of duplicate code removed) - Unify log color configuration with LogTheme class in gui/log_config.py - Create permission check decorators (@require_admin, @require_permission, @require_user_type, @handle_errors) - Update all dialog classes to use unified patterns Phase 2: Architecture Improvements - Create input validation framework (ValidationResult, Validator, ValidatedWidget classes) - Implement StateManager with observer pattern for component state sharing - Create unified ErrorHandler for consistent error handling - Extract CheckboxTreeview into reusable component New Modules: - gui/widgets/base_dialog.py - Base dialog class with modal setup and centering - gui/utils/decorators.py - Permission and error handling decorators - gui/utils/error_handler.py - Unified error handling with user-friendly messages - gui/utils/state_manager.py - State management with observer pattern - gui/utils/validators.py - Input validation framework - gui/material_validation/checkbox_treeview.py - Reusable checkbox table Modified Files: - gui/log_config.py - Added LogTheme class for centralized styling - gui/widgets/log_text.py - Use LogTheme.COLORS - gui/widgets/progress_dialog.py - Inherit from BaseDialog - gui/widgets/delete_progress_window.py - Inherit from BaseDialog, use LogTheme - gui/widgets/__init__.py - Add new exports, optional imports - gui/login_dialog.py - Use unified centering pattern - gui/user_selection_dialog.py - Use unified centering pattern - gui/material_validation_tab.py - Import CheckboxTreeview from new module Benefits: - Reduced code duplication by ~200 lines - Improved maintainability through centralized configuration - Better abstraction with 5 new reusable base classes - Enhanced type safety with type hints - Future-proof theming support via LogTheme Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
202
gui/utils/error_handler.py
Normal file
202
gui/utils/error_handler.py
Normal file
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Error Handler - 统一的错误处理器
|
||||
|
||||
提供统一的错误处理机制,将技术错误转换为用户友好的消息
|
||||
"""
|
||||
import logging
|
||||
import traceback
|
||||
from tkinter import messagebox
|
||||
from typing import Optional, Type
|
||||
import sys
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ErrorHandler:
|
||||
"""
|
||||
统一的错误处理器
|
||||
|
||||
将技术错误转换为用户友好的消息,并记录详细日志
|
||||
"""
|
||||
|
||||
# 错误类型到用户消息的映射
|
||||
ERROR_MESSAGE_MAP = {
|
||||
ConnectionError: "无法连接到服务器,请检查网络连接",
|
||||
PermissionError: "权限不足,请联系管理员",
|
||||
FileNotFoundError: "找不到指定的文件",
|
||||
ValueError: "输入数据格式不正确",
|
||||
TypeError: "数据类型错误,请检查输入",
|
||||
KeyError: "数据缺失,请检查输入完整性",
|
||||
TimeoutError: "操作超时,请重试",
|
||||
RuntimeError: "运行时错误,请查看日志了解详情",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def handle(
|
||||
error: Exception,
|
||||
context: str = "",
|
||||
show_user: bool = True,
|
||||
parent=None
|
||||
):
|
||||
"""
|
||||
处理错误
|
||||
|
||||
记录详细日志,并可选择向用户显示友好的错误消息
|
||||
|
||||
Args:
|
||||
error: 异常对象
|
||||
context: 错误发生的上下文信息
|
||||
show_user: 是否向用户显示错误消息
|
||||
parent: 父窗口(用于显示消息框)
|
||||
"""
|
||||
# 记录详细日志
|
||||
error_message = str(error)
|
||||
if context:
|
||||
logger.error(f"{context}: {error_message}", exc_info=error)
|
||||
else:
|
||||
logger.error(error_message, exc_info=error)
|
||||
|
||||
# 显示用户友好的错误信息
|
||||
if show_user:
|
||||
user_message = ErrorHandler._get_user_message(error)
|
||||
if context:
|
||||
user_message = f"[{context}]\n{user_message}"
|
||||
|
||||
if parent:
|
||||
messagebox.showerror("操作失败", user_message, parent=parent)
|
||||
else:
|
||||
messagebox.showerror("操作失败", user_message)
|
||||
|
||||
@staticmethod
|
||||
def _get_user_message(error: Exception) -> str:
|
||||
"""
|
||||
将技术错误转换为用户友好的消息
|
||||
|
||||
Args:
|
||||
error: 异常对象
|
||||
|
||||
Returns:
|
||||
用户友好的错误消息
|
||||
"""
|
||||
# 检查是否为已知错误类型
|
||||
for error_type, message in ErrorHandler.ERROR_MESSAGE_MAP.items():
|
||||
if isinstance(error, error_type):
|
||||
return message
|
||||
|
||||
# 未知错误类型
|
||||
error_name = type(error).__name__
|
||||
error_msg = str(error)
|
||||
|
||||
# 如果错误消息为空或只包含类名,返回通用消息
|
||||
if not error_msg or error_msg == error_name:
|
||||
return "操作失败,请查看日志了解详情"
|
||||
|
||||
# 返回简化的错误消息(不包含技术细节)
|
||||
# 限制长度以避免消息过长
|
||||
if len(error_msg) > 200:
|
||||
return f"{error_msg[:200]}..."
|
||||
|
||||
return error_msg
|
||||
|
||||
@staticmethod
|
||||
def handle_with_retry(
|
||||
error: Exception,
|
||||
context: str = "",
|
||||
retry_callback: Optional[callable] = None,
|
||||
parent=None
|
||||
) -> bool:
|
||||
"""
|
||||
处理错误并提供重试选项
|
||||
|
||||
Args:
|
||||
error: 异常对象
|
||||
context: 错误发生的上下文信息
|
||||
retry_callback: 重试回调函数
|
||||
parent: 父窗口
|
||||
|
||||
Returns:
|
||||
True 如果用户选择重试,False 否则
|
||||
"""
|
||||
user_message = ErrorHandler._get_user_message(error)
|
||||
if context:
|
||||
user_message = f"[{context}]\n{user_message}"
|
||||
|
||||
user_message += "\n\n是否重试?"
|
||||
|
||||
if parent:
|
||||
result = messagebox.askyesno("操作失败", user_message, parent=parent)
|
||||
else:
|
||||
result = messagebox.askyesno("操作失败", user_message)
|
||||
|
||||
if result and retry_callback:
|
||||
try:
|
||||
retry_callback()
|
||||
return True
|
||||
except Exception as e:
|
||||
ErrorHandler.handle(e, f"{context} (重试)", True, parent)
|
||||
return False
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def log_exception(error: Exception, context: str = ""):
|
||||
"""
|
||||
仅记录异常到日志,不显示用户消息
|
||||
|
||||
Args:
|
||||
error: 异常对象
|
||||
context: 错误发生的上下文信息
|
||||
"""
|
||||
error_message = str(error)
|
||||
if context:
|
||||
logger.error(f"{context}: {error_message}", exc_info=error)
|
||||
else:
|
||||
logger.error(error_message, exc_info=error)
|
||||
|
||||
@staticmethod
|
||||
def show_warning(message: str, parent=None):
|
||||
"""
|
||||
显示警告消息
|
||||
|
||||
Args:
|
||||
message: 警告消息
|
||||
parent: 父窗口
|
||||
"""
|
||||
if parent:
|
||||
messagebox.showwarning("警告", message, parent=parent)
|
||||
else:
|
||||
messagebox.showwarning("警告", message)
|
||||
|
||||
@staticmethod
|
||||
def show_info(message: str, parent=None):
|
||||
"""
|
||||
显示信息消息
|
||||
|
||||
Args:
|
||||
message: 信息消息
|
||||
parent: 父窗口
|
||||
"""
|
||||
if parent:
|
||||
messagebox.showinfo("信息", message, parent=parent)
|
||||
else:
|
||||
messagebox.showinfo("信息", message)
|
||||
|
||||
@staticmethod
|
||||
def ask_confirmation(message: str, parent=None) -> bool:
|
||||
"""
|
||||
询问用户确认
|
||||
|
||||
Args:
|
||||
message: 确认消息
|
||||
parent: 父窗口
|
||||
|
||||
Returns:
|
||||
True 如果用户确认,False 否则
|
||||
"""
|
||||
if parent:
|
||||
return messagebox.askyesno("确认", message, parent=parent)
|
||||
else:
|
||||
return messagebox.askyesno("确认", message)
|
||||
Reference in New Issue
Block a user