Files
playwrite/gui/utils/error_handler.py
Misaka Server f715cb97e3 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>
2026-02-26 18:40:26 +08:00

203 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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)