#!/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 class ToolTip: """给组件添加悬停提示""" def __init__(self, widget, text): self.widget = widget self.text = text self.tip_window = None self.widget.bind("", self.show_tip) self.widget.bind("", self.hide_tip) def show_tip(self, event=None): if self.tip_window or not self.text: return x, y, _cx, cy = self.widget.bbox("insert") x = x + self.widget.winfo_rootx() + 27 y = y + cy + self.widget.winfo_rooty() + 27 self.tip_window = tw = tk.Toplevel(self.widget) tw.wm_overrideredirect(1) tw.wm_geometry("+%d+%d" % (x, y)) label = tk.Label(tw, text=self.text, justify=tk.LEFT, background="#ffffe0", relief=tk.SOLID, borderwidth=1, font=("tahoma", "8", "normal"), padx=4, pady=2) label.pack(ipadx=1) def hide_tip(self, event=None): tw = self.tip_window self.tip_window = None if tw: tw.destroy() 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("", toggle_collapse) state_label.bind("", toggle_collapse) title_label.bind("", 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))