#!/usr/bin/env python # -*- coding: utf-8 -*- """GUI日志处理模块""" import logging import queue import sys import tkinter as tk # 全局日志队列,用于异步更新UI LOG_QUEUE = queue.Queue() class LogRedirector: """日志重定向器,用于捕获命令输出并显示到界面""" def __init__(self, text_widget): self.text_widget = text_widget self.buffer = "" self.terminal = sys.__stdout__ def write(self, string): self.buffer += string self.terminal.write(string) self.text_widget.after(0, self.update_text_widget) def update_text_widget(self): self.text_widget.configure(state=tk.NORMAL) if self.buffer.strip(): if any(marker in self.buffer.lower() for marker in ["错误", "error", "失败", "异常", "exception"]): self.text_widget.insert(tk.END, self.buffer, "error") elif any(marker in self.buffer.lower() for marker in ["警告", "warning"]): self.text_widget.insert(tk.END, self.buffer, "warning") elif any(marker in self.buffer.lower() for marker in ["成功", "success", "完成", "成功处理"]): self.text_widget.insert(tk.END, self.buffer, "success") elif any(marker in self.buffer.lower() for marker in ["info", "信息", "开始", "处理中"]): self.text_widget.insert(tk.END, self.buffer, "info") else: self.text_widget.insert(tk.END, self.buffer, "normal") else: self.text_widget.insert(tk.END, self.buffer) self.text_widget.see(tk.END) self.text_widget.configure(state=tk.DISABLED) self.buffer = "" def flush(self): self.terminal.flush() class GUILogHandler(logging.Handler): """自定义日志处理器,将日志放入队列,由GUI主线程定时消费""" def __init__(self, text_widget): super().__init__() self.text_widget = text_widget def emit(self, record): try: msg = self.format(record) if record.levelno >= logging.ERROR: tag = "error" elif record.levelno >= logging.WARNING: tag = "warning" elif record.levelno >= logging.INFO: tag = "info" else: tag = "normal" LOG_QUEUE.put((msg + "\n", tag)) except Exception: self.handleError(record) def poll_log_queue(text_widget): """定期从队列中读取日志并更新UI""" try: updated = False while not LOG_QUEUE.empty(): msg, tag = LOG_QUEUE.get_nowait() text_widget.configure(state=tk.NORMAL) text_widget.insert(tk.END, msg, tag) updated = True if updated: text_widget.see(tk.END) text_widget.configure(state=tk.DISABLED) except Exception: pass finally: text_widget.after(100, lambda: poll_log_queue(text_widget)) def init_gui_logger(text_widget, level=logging.INFO): handler = GUILogHandler(text_widget) handler.setLevel(level) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) root_logger = logging.getLogger() for h in root_logger.handlers[:]: if isinstance(h, logging.StreamHandler): root_logger.removeHandler(h) if not any(isinstance(h, GUILogHandler) for h in root_logger.handlers): root_logger.addHandler(handler) root_logger.setLevel(level) return handler def dispose_gui_logger(): root_logger = logging.getLogger() for handler in root_logger.handlers[:]: if isinstance(handler, GUILogHandler): root_logger.removeHandler(handler) try: handler.close() except Exception: pass def add_to_log(log_widget, text, tag="normal"): """向日志队列添加文本,由 poll_log_queue 消费并更新 UI""" if log_widget is None: print(f"[{tag}] {text}", end="") return LOG_QUEUE.put((text, tag))