Files
orc-order-v2/app/ui/ui_widgets.py
T

152 lines
4.9 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""UI控件模块 - StatusBar、ProgressReporter、可折叠框架等"""
import tkinter as tk
from tkinter import ttk
from app.ui.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("<Enter>", self.show_tip)
self.widget.bind("<Leave>", 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("<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))