e4d62df7e3
- 智能供应商识别(蓉城易购/烟草/杨碧月/通用) - 百度 OCR 表格识别集成 - 规则引擎(列映射/数据清洗/单位转换/规格推断) - 条码映射管理与云端同步(Gitea REST API) - 云端同步支持:条码映射、供应商配置、商品资料、采购模板 - 拖拽一键处理(图片→OCR→Excel→合并) - 191 个单元测试 - 移除无用的模板管理功能 - 清理 IDE 产物目录 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
122 lines
3.9 KiB
Python
122 lines
3.9 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""UI控件模块 - StatusBar、ProgressReporter、可折叠框架等"""
|
|
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
|
|
from .theme import THEMES, get_theme_mode
|
|
|
|
|
|
class StatusBar(tk.Frame):
|
|
"""状态栏,显示当前系统状态和进度"""
|
|
|
|
def __init__(self, master, **kwargs):
|
|
super().__init__(master, **kwargs)
|
|
self.configure(height=25, relief=tk.SUNKEN, borderwidth=1)
|
|
|
|
self.status_label = tk.Label(self, text="就绪", anchor=tk.W, padx=5)
|
|
self.status_label.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
|
|
|
self.progress = ttk.Progressbar(self, orient=tk.HORIZONTAL, length=200, mode='determinate')
|
|
self.progress.pack(side=tk.RIGHT, padx=5, pady=2)
|
|
|
|
self.progress.pack_forget()
|
|
|
|
def set_status(self, text, progress=None):
|
|
"""设置状态栏文本和进度"""
|
|
self.status_label.config(text=text)
|
|
|
|
if progress is not None and 0 <= progress <= 100:
|
|
self.progress.pack(side=tk.RIGHT, padx=5, pady=2)
|
|
self.progress.config(value=progress)
|
|
else:
|
|
self.progress.pack_forget()
|
|
|
|
def set_running(self, is_running=True):
|
|
"""设置运行状态"""
|
|
theme = THEMES[get_theme_mode()]
|
|
if is_running:
|
|
self.status_label.config(text="处理中...", foreground=theme["info"])
|
|
self.progress.pack(side=tk.RIGHT, padx=5, pady=2)
|
|
self.progress.config(mode='indeterminate')
|
|
self.progress.start()
|
|
else:
|
|
self.status_label.config(text="就绪", foreground=theme["fg"])
|
|
self.progress.stop()
|
|
self.progress.pack_forget()
|
|
|
|
|
|
class ProgressReporter:
|
|
def __init__(self, status_bar: StatusBar):
|
|
self.status_bar = status_bar
|
|
|
|
def set(self, text: str, percent: int = None):
|
|
try:
|
|
if percent is not None:
|
|
self.status_bar.set_status(text, percent)
|
|
else:
|
|
self.status_bar.set_status(text)
|
|
except Exception:
|
|
pass
|
|
|
|
def running(self):
|
|
try:
|
|
self.status_bar.set_running(True)
|
|
except Exception:
|
|
pass
|
|
|
|
def done(self):
|
|
try:
|
|
self.status_bar.set_running(False)
|
|
self.status_bar.set_status("就绪")
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def create_collapsible_frame(parent, title, initial_state=True):
|
|
"""创建可折叠的面板"""
|
|
frame = tk.Frame(parent)
|
|
frame.pack(fill=tk.X, pady=5)
|
|
|
|
title_frame = tk.Frame(frame)
|
|
title_frame.pack(fill=tk.X)
|
|
|
|
state_var = tk.BooleanVar(value=initial_state)
|
|
indicator = "▼" if initial_state else "►"
|
|
state_label = tk.Label(title_frame, text=indicator, font=("Arial", 10, "bold"))
|
|
state_label.pack(side=tk.LEFT, padx=5)
|
|
|
|
title_label = tk.Label(title_frame, text=title, font=("Arial", 11, "bold"))
|
|
title_label.pack(side=tk.LEFT, padx=5)
|
|
|
|
content_frame = tk.Frame(frame)
|
|
if initial_state:
|
|
content_frame.pack(fill=tk.X, padx=20, pady=5)
|
|
|
|
def toggle_collapse(event=None):
|
|
current_state = state_var.get()
|
|
new_state = not current_state
|
|
state_var.set(new_state)
|
|
state_label.config(text="▼" if new_state else "►")
|
|
if new_state:
|
|
content_frame.pack(fill=tk.X, padx=20, pady=5)
|
|
else:
|
|
content_frame.pack_forget()
|
|
|
|
title_frame.bind("<Button-1>", toggle_collapse)
|
|
state_label.bind("<Button-1>", toggle_collapse)
|
|
title_label.bind("<Button-1>", toggle_collapse)
|
|
|
|
return content_frame, state_var
|
|
|
|
|
|
def center_window(window):
|
|
"""使窗口居中显示"""
|
|
window.update_idletasks()
|
|
width = window.winfo_width()
|
|
height = window.winfo_height()
|
|
x = (window.winfo_screenwidth() // 2) - (width // 2)
|
|
y = (window.winfo_screenheight() // 2) - (height // 2)
|
|
window.geometry('{}x{}+{}+{}'.format(width, height, x, y))
|