refactor: extract common tab functionality to BaseTab and add decorators
- Add BaseTab base class for common tab initialization and logging - Add constants module for shared GUI constants - Move CheckboxTreeview to separate widget file - Add admin_only and require_session decorators to utils.py - Refactor DataExtractionTab and MaterialValidationTab to inherit BaseTab - Remove duplicate _update_log method from tab classes Co-Authored-By: Claude (glm-5) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
数据提取标签页 - 稳定性修复版
|
||||
数据提取标签页
|
||||
|
||||
修复了 LogText.info 不支持 add_timestamp 参数导致的 TypeError。
|
||||
从 ERP 系统提取生产订单数据的标签页。
|
||||
继承自 BaseTab,使用统一的日志系统。
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -14,6 +15,7 @@ import queue
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
from pathlib import Path
|
||||
from gui.base_tab import BaseTab
|
||||
from gui.widgets import FileSelector, LogText, ProductionIdInput, GuiTextHandler
|
||||
from gui.config_manager import ConfigManager
|
||||
from gui.log_config import setup_gui_logging, get_logger
|
||||
@@ -21,13 +23,11 @@ from gui.progress import ProgressInfo, ProgressCalculator
|
||||
from gui.utils import RealtimeOutput
|
||||
|
||||
|
||||
class DataExtractionTab(ttk.Frame):
|
||||
class DataExtractionTab(BaseTab):
|
||||
"""数据提取标签页"""
|
||||
|
||||
def __init__(self, parent, config: ConfigManager, main_window=None):
|
||||
super().__init__(parent)
|
||||
self.config = config
|
||||
self.main_window = main_window
|
||||
super().__init__(parent, config, main_window)
|
||||
self.extracting = False
|
||||
self.extractor = None
|
||||
self.extraction_thread = None
|
||||
@@ -42,10 +42,11 @@ class DataExtractionTab(ttk.Frame):
|
||||
self.create_widgets()
|
||||
self._apply_ui_config()
|
||||
|
||||
# 初始化日志消息
|
||||
try:
|
||||
self.log_text.info("数据提取标签页已就绪")
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
self.logger.debug(f"初始化日志消息失败: {e}")
|
||||
|
||||
def create_widgets(self):
|
||||
horizontal_paned = ttk.PanedWindow(self, orient=tk.HORIZONTAL)
|
||||
@@ -137,11 +138,14 @@ class DataExtractionTab(ttk.Frame):
|
||||
self.production_id_input.apply_font(font_family, font_size)
|
||||
if hasattr(self.log_text, 'apply_font'):
|
||||
self.log_text.apply_font(font_family, font_size)
|
||||
except: pass
|
||||
except Exception as e:
|
||||
self.logger.debug(f"应用 UI 配置失败: {e}")
|
||||
|
||||
def _set_pane_width(self, width: int):
|
||||
try: self.horizontal_paned.sashpos(0, width)
|
||||
except: pass
|
||||
try:
|
||||
self.horizontal_paned.sashpos(0, width)
|
||||
except tk.TclError as e:
|
||||
self.logger.debug(f"设置窗格宽度失败: {e}")
|
||||
|
||||
def start_extraction(self):
|
||||
production_ids = self.production_id_input.get()
|
||||
@@ -208,8 +212,10 @@ class DataExtractionTab(ttk.Frame):
|
||||
self._update_log(f"运行时错误: {str(e)}", "ERROR")
|
||||
finally:
|
||||
if temp_file and os.path.exists(temp_file):
|
||||
try: os.unlink(temp_file)
|
||||
except: pass
|
||||
try:
|
||||
os.unlink(temp_file)
|
||||
except OSError as e:
|
||||
self.logger.debug(f"清理临时文件失败: {e}")
|
||||
self.after(0, self._extraction_complete)
|
||||
|
||||
def _extraction_complete(self):
|
||||
@@ -225,32 +231,18 @@ class DataExtractionTab(ttk.Frame):
|
||||
value, message = self.progress_queue.get_nowait()
|
||||
self.progress_bar["value"] = value
|
||||
self.status_label.config(text=message)
|
||||
except queue.Empty: break
|
||||
finally: self.after(50, self._poll_progress_queue)
|
||||
except queue.Empty:
|
||||
break
|
||||
except Exception as e:
|
||||
self.logger.debug(f"轮询进度队列失败: {e}")
|
||||
finally:
|
||||
self.after(50, self._poll_progress_queue)
|
||||
|
||||
def _update_progress(self, value: int, message: str):
|
||||
try: self.progress_queue.put_nowait((value, message))
|
||||
except: pass
|
||||
|
||||
def _update_log(self, message: str, level: str = "INFO"):
|
||||
"""
|
||||
标准的日志更新方法(兼容接口)
|
||||
|
||||
通过统一的 logging 系统输出日志,自动同时输出到控制台和 GUI。
|
||||
|
||||
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)
|
||||
try:
|
||||
self.progress_queue.put_nowait((value, message))
|
||||
except queue.Full as e:
|
||||
self.logger.debug(f"进度队列已满: {e}")
|
||||
|
||||
def _on_production_ids_changed(self, event=None):
|
||||
if self.main_window:
|
||||
|
||||
Reference in New Issue
Block a user