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:
132
gui/base_tab.py
Normal file
132
gui/base_tab.py
Normal file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
BaseTab - 标签页基类
|
||||
|
||||
提供所有标签页共享的通用功能,包括:
|
||||
- 统一的日志更新方法
|
||||
- 线程安全的 GUI 操作
|
||||
- 通用工具方法
|
||||
"""
|
||||
|
||||
import logging
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
|
||||
class BaseTab(ttk.Frame):
|
||||
"""标签页基类
|
||||
|
||||
提供所有标签页共享的通用功能。
|
||||
子类应继承此类并实现 create_widgets 方法。
|
||||
"""
|
||||
|
||||
def __init__(self, parent, config=None, main_window=None):
|
||||
"""初始化基类
|
||||
|
||||
Args:
|
||||
parent: 父容器
|
||||
config: 配置管理器(可选)
|
||||
main_window: 主窗口引用(可选)
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.config = config
|
||||
self.main_window = main_window
|
||||
|
||||
def _update_log(self, message: str, level: str = "INFO"):
|
||||
"""
|
||||
线程安全的日志更新方法
|
||||
|
||||
通过统一的 logging 系统输出日志,自动同时输出到控制台和 GUI。
|
||||
|
||||
Args:
|
||||
message: 日志消息
|
||||
level: 日志级别 (INFO, SUCCESS, WARNING, ERROR, DEBUG)
|
||||
|
||||
Note:
|
||||
子类需要设置 self.logger 和 self._gui_handler 才能使用此方法。
|
||||
如果 self.logger 未设置,将使用 logging.getLogger(__name__) 作为后备。
|
||||
"""
|
||||
# 获取 logger(优先使用实例的 logger,否则使用模块 logger)
|
||||
logger = getattr(self, 'logger', None) or logging.getLogger(__name__)
|
||||
|
||||
# 将自定义级别映射到 logging 级别
|
||||
level_upper = level.upper()
|
||||
if level_upper == "SUCCESS":
|
||||
# SUCCESS 映射到 INFO,但在 UI 中仍显示为 SUCCESS
|
||||
logger.info(message)
|
||||
else:
|
||||
# 其他级别直接映射
|
||||
log_level = getattr(logging, level_upper, logging.INFO)
|
||||
logger.log(log_level, message)
|
||||
|
||||
def _run_on_main_thread(self, callback, *args, **kwargs):
|
||||
"""在主线程中执行回调函数(线程安全)
|
||||
|
||||
Args:
|
||||
callback: 要执行的回调函数
|
||||
*args: 位置参数
|
||||
**kwargs: 关键字参数
|
||||
|
||||
Note:
|
||||
使用 after(0, ...) 确保在主线程中执行。
|
||||
"""
|
||||
self.after(0, lambda: callback(*args, **kwargs))
|
||||
|
||||
def _is_admin(self) -> bool:
|
||||
"""检查当前用户是否为管理员
|
||||
|
||||
Returns:
|
||||
bool: 如果是管理员返回 True,否则返回 False
|
||||
|
||||
Note:
|
||||
需要 main_window 或 session_manager 支持。
|
||||
"""
|
||||
# 尝试从 session_manager 获取
|
||||
if hasattr(self, 'session_manager') and self.session_manager:
|
||||
return self.session_manager.is_admin()
|
||||
|
||||
# 尝试从 main_window 获取
|
||||
if self.main_window and hasattr(self.main_window, 'session_manager'):
|
||||
return self.main_window.session_manager.is_admin()
|
||||
|
||||
return False
|
||||
|
||||
def _get_username(self) -> str:
|
||||
"""获取当前用户名
|
||||
|
||||
Returns:
|
||||
str: 当前用户名,如果无法获取则返回空字符串
|
||||
|
||||
Note:
|
||||
需要 session_manager 支持。
|
||||
"""
|
||||
if hasattr(self, 'session_manager') and self.session_manager:
|
||||
return self.session_manager.get_username() or ""
|
||||
|
||||
if self.main_window and hasattr(self.main_window, 'session_manager'):
|
||||
return self.main_window.session_manager.get_username() or ""
|
||||
|
||||
return ""
|
||||
|
||||
def set_busy(self, busy: bool):
|
||||
"""设置窗口忙碌状态(显示等待光标)
|
||||
|
||||
Args:
|
||||
busy: True 显示等待光标,False 恢复正常光标
|
||||
"""
|
||||
try:
|
||||
cursor = "watch" if busy else "arrow"
|
||||
# 获取顶层窗口
|
||||
toplevel = self.winfo_toplevel()
|
||||
if toplevel:
|
||||
toplevel.config(cursor=cursor)
|
||||
toplevel.update()
|
||||
except tk.TclError:
|
||||
# 窗口可能已被销毁
|
||||
pass
|
||||
|
||||
def reload_config(self):
|
||||
"""重新加载配置(子类可覆盖此方法)"""
|
||||
if self.config and hasattr(self.config, 'reload'):
|
||||
self.config.reload()
|
||||
Reference in New Issue
Block a user