feat: implement unified logging system for GUI components

Add centralized logging mechanism that simultaneously outputs to console
and GUI log components, improving code maintainability and consistency.

Changes:
- Add gui/log_config.py for centralized logging configuration
- Add gui/widgets/log_handler.py as bridge between logging and LogText
- Integrate unified logging into DataExtractionTab and MaterialValidationTab
- Initialize logging system in MainWindow on startup
- Improve error messages in material_status_validator for empty results
- Add documentation for logging mechanism and refactoring

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Misaka
2026-02-24 20:31:59 +08:00
parent 3c45ef58d1
commit 24053c6a3b
9 changed files with 821 additions and 24 deletions

View File

@@ -11,14 +11,16 @@
import os
import threading
import logging
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, simpledialog
from pathlib import Path
from io import StringIO
from contextlib import redirect_stdout
from typing import List, Dict
from gui.widgets import FileSelector, LogText
from gui.widgets import FileSelector, LogText, GuiTextHandler
from gui.config_manager import ConfigManager
from gui.log_config import setup_gui_logging, get_logger
from gui.material_type_management_dialog import MaterialTypeManagementDialog
from db.materials_to_be_deleted_dao import MaterialsTypeToBeDeletedDAO
import pandas as pd
@@ -156,6 +158,10 @@ class MaterialValidationTab(ttk.Frame):
self.select_all_managers_var: tk.BooleanVar = tk.BooleanVar(value=True) # 全选复选框状态
self.previously_selected_managers: List[str] = [] # 保存筛选状态
# 初始化统一日志系统
self.logger = get_logger(__name__)
self._gui_handler = None # 将在 _create_log_panel 中设置
self.create_widgets()
# 稍后显示就绪消息
@@ -379,6 +385,14 @@ class MaterialValidationTab(ttk.Frame):
self.log_text = LogText(parent, height=8, readonly=True)
self.log_text.pack(fill=tk.BOTH, expand=True)
# 设置 GUI 日志处理器,将 logging 输出桥接到 LogText 组件
self._gui_handler = GuiTextHandler(self.log_text)
self._gui_handler.setFormatter(logging.Formatter(
'%(asctime)s [%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
))
self.logger.addHandler(self._gui_handler)
def _on_source_mode_change(self):
"""数据源模式切换"""
# PERMISSION CHECK: 仅管理员用户可以选择数据源模式
@@ -895,10 +909,13 @@ class MaterialValidationTab(ttk.Frame):
if line.strip():
self._update_log(line, "INFO")
if result_file and results:
if result_file and results and len(results) > 0:
self._update_log(f"校验完成,结果已保存到:{output_file}", "SUCCESS")
# 加载结果显示(带删除状态)
self._load_results_with_deletion_status(result_file)
elif results is not None and len(results) == 0:
# 已经在 validator 中输出详细错误信息,这里只做简单提示
self._update_log("校验失败:未找到物料记录,请查看上方日志了解详细原因", "ERROR")
else:
self._update_log("校验失败", "ERROR")
@@ -1159,19 +1176,24 @@ class MaterialValidationTab(ttk.Frame):
self._initialize_manager_filter()
def _update_log(self, message: str, level: str = "INFO"):
"""线程安全的日志更新"""
"""
线程安全的日志更新(兼容接口)
def update():
if level == "INFO":
self.log_text.info(message)
elif level == "SUCCESS":
self.log_text.success(message)
elif level == "WARNING":
self.log_text.warning(message)
elif level == "ERROR":
self.log_text.error(message)
通过统一的 logging 系统输出日志,自动同时输出到控制台和 GUI。
self.after(0, update)
Args:
message: 日志消息
level: 日志级别 (INFO, SUCCESS, WARNING, ERROR, DEBUG)
"""
# 将自定义级别映射到 logging 级别
level_upper = level.upper()
if level_upper == "SUCCESS":
# SUCCESS 映射到 INFO但在 UI 中仍显示为 SUCCESS
self.logger.info(message)
else:
# 其他级别直接映射
log_level = getattr(logging, level_upper, logging.INFO)
self.logger.log(log_level, message)
def export_results(self):
"""导出结果到 Excel"""