feat: 益选 OCR 订单处理系统初始提交
- 智能供应商识别(蓉城易购/烟草/杨碧月/通用) - 百度 OCR 表格识别集成 - 规则引擎(列映射/数据清洗/单位转换/规格推断) - 条码映射管理与云端同步(Gitea REST API) - 云端同步支持:条码映射、供应商配置、商品资料、采购模板 - 拖拽一键处理(图片→OCR→Excel→合并) - 191 个单元测试 - 移除无用的模板管理功能 - 清理 IDE 产物目录 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
#!/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))
|
||||
Reference in New Issue
Block a user