fix:添加UI界面,完善功能
This commit is contained in:
199
UI.py
Normal file
199
UI.py
Normal file
@@ -0,0 +1,199 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from tkinter import scrolledtext
|
||||
from tkinter import messagebox
|
||||
import threading
|
||||
import sys
|
||||
import time
|
||||
import queue # 【新增】引入队列库
|
||||
import re # 【新增】用于去除 ANSI 颜色代码
|
||||
|
||||
from loguru import logger # 【新增】需要导入 logger 来配置它
|
||||
from config.config import load_config
|
||||
|
||||
# 假设你的功能函数都在这里
|
||||
from utils.generate_utils import (
|
||||
generate_template,
|
||||
generate_comment_all,
|
||||
batch_convert_folder,
|
||||
generate_report,
|
||||
generate_zodiac,
|
||||
)
|
||||
from utils.file_utils import export_data_folder, initialize_project
|
||||
|
||||
# ==========================================
|
||||
# 0. 全局配置与队列准备
|
||||
# ==========================================
|
||||
config = load_config("config.toml")
|
||||
|
||||
# 【新增】创建一个全局队列,用于存放日志消息
|
||||
log_queue = queue.Queue()
|
||||
|
||||
|
||||
def ansi_cleaner(text):
|
||||
"""【辅助函数】去除 loguru 输出中的 ANSI 颜色代码 (例如 \x1b[32m)"""
|
||||
ansi_escape = re.compile(r"\x1b\[[0-9;]*m")
|
||||
return ansi_escape.sub("", text)
|
||||
|
||||
|
||||
def queue_sink(message):
|
||||
"""【核心】这是一个 loguru 的 sink 回调函数
|
||||
当 logger.info() 被调用时,loguru 会把格式化好的消息传给这个函数
|
||||
"""
|
||||
# message 是一个对象,我们将其转换为字符串并放入队列
|
||||
# 使用 ansi_cleaner 去掉颜色代码,否则 GUI 里会有乱码
|
||||
clean_msg = ansi_cleaner(message)
|
||||
log_queue.put(clean_msg)
|
||||
|
||||
|
||||
# ==========================================
|
||||
# GUI 主程序类
|
||||
# ==========================================
|
||||
class ReportApp:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("🌱 幼儿园成长报告助手")
|
||||
self.root.geometry("700x600") # 稍微调大一点
|
||||
|
||||
# 设置样式
|
||||
self.style = ttk.Style()
|
||||
self.style.theme_use("clam")
|
||||
self.style.configure("TButton", font=("微软雅黑", 10), padding=5)
|
||||
self.style.configure(
|
||||
"Title.TLabel", font=("微软雅黑", 16, "bold"), foreground="#2E8B57"
|
||||
)
|
||||
self.style.configure("Sub.TLabel", font=("微软雅黑", 9), foreground="gray")
|
||||
|
||||
# --- 1. 标题区域 ---
|
||||
header_frame = ttk.Frame(root, padding="10 20 10 10")
|
||||
header_frame.pack(fill=tk.X)
|
||||
ttk.Label(
|
||||
header_frame, text="🌱 幼儿园成长报告助手", style="Title.TLabel"
|
||||
).pack()
|
||||
ttk.Label(header_frame, text="By 寒寒", style="Sub.TLabel").pack()
|
||||
|
||||
# --- 2. 按钮功能区域 ---
|
||||
btn_frame = ttk.Frame(root, padding=10)
|
||||
btn_frame.pack(fill=tk.X)
|
||||
|
||||
buttons = [
|
||||
("1. 📁 生成图片路径", self.run_generate_folders),
|
||||
("2. 🤖 生成评语 (AI)", self.run_generate_comments),
|
||||
("3. 📊 生成报告 (PPT)", self.run_generate_report),
|
||||
("4. 📑 格式转换 (PDF)", self.run_convert_pdf),
|
||||
("5. 🐂 生肖转化 (生日)", self.run_zodiac),
|
||||
("6. 📦 导出数据模板 (Zip)", self.run_export_data_folder),
|
||||
("7. 📤 初始化系统", self.run_initialize_project),
|
||||
("8. 🚪 退出系统", self.quit_app),
|
||||
]
|
||||
|
||||
for index, (text, func) in enumerate(buttons):
|
||||
btn = ttk.Button(btn_frame, text=text, command=func)
|
||||
r, c = divmod(index, 2)
|
||||
btn.grid(row=r, column=c, padx=10, pady=5, sticky="ew")
|
||||
|
||||
btn_frame.columnconfigure(0, weight=1)
|
||||
btn_frame.columnconfigure(1, weight=1)
|
||||
|
||||
# --- 3. 日志输出区域 ---
|
||||
log_frame = ttk.LabelFrame(root, text="系统实时日志", padding=10)
|
||||
log_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
|
||||
# 注意:这里字体设为 Consolas 或其他等宽字体,看起来更像代码日志
|
||||
self.log_text = scrolledtext.ScrolledText(
|
||||
log_frame, height=10, state="disabled", font=("Consolas", 9)
|
||||
)
|
||||
self.log_text.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# 【新增】启动日志轮询
|
||||
# 告诉 GUI:每隔 100ms 检查一下队列里有没有新日志
|
||||
self.root.after(100, self.poll_log_queue)
|
||||
|
||||
logger.info("GUI 初始化完成,等待指令...")
|
||||
|
||||
# --- 【新增】核心方法:轮询队列 ---
|
||||
def poll_log_queue(self):
|
||||
"""检查队列中是否有新日志,如果有则显示到界面上"""
|
||||
while not log_queue.empty():
|
||||
try:
|
||||
# 不阻塞地获取消息
|
||||
msg = log_queue.get_nowait()
|
||||
|
||||
# 写入 GUI
|
||||
self.log_text.config(state="normal")
|
||||
self.log_text.insert(tk.END, msg) # msg 已经包含了换行符
|
||||
self.log_text.see(tk.END) # 自动滚动到底部
|
||||
self.log_text.config(state="disabled")
|
||||
except queue.Empty:
|
||||
break
|
||||
|
||||
# 递归调用:100ms 后再次执行自己
|
||||
self.root.after(100, self.poll_log_queue)
|
||||
|
||||
# --- 辅助方法:线程包装器 ---
|
||||
def run_in_thread(self, target_func, *args):
|
||||
"""在单独的线程中运行函数"""
|
||||
|
||||
def thread_task():
|
||||
try:
|
||||
# 这里不需要手动 self.log 了,因为 target_func 内部的 logger 会自动触发 sink
|
||||
target_func(*args)
|
||||
logger.success("✅ 当前任务执行完毕。") # 使用 logger 输出成功
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 发生错误: {str(e)}") # 使用 logger 输出错误
|
||||
import traceback
|
||||
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
threading.Thread(target=thread_task, daemon=True).start()
|
||||
|
||||
# ==========================================
|
||||
# 按钮事件 (不需要改动,逻辑都在 thread_task 里处理了)
|
||||
# ==========================================
|
||||
def run_generate_folders(self):
|
||||
self.run_in_thread(generate_template)
|
||||
|
||||
def run_generate_comments(self):
|
||||
self.run_in_thread(generate_comment_all)
|
||||
|
||||
def run_generate_report(self):
|
||||
self.run_in_thread(generate_report)
|
||||
|
||||
def run_convert_pdf(self):
|
||||
# 传入 output_folder
|
||||
self.run_in_thread(batch_convert_folder, config["output_folder"])
|
||||
|
||||
def run_zodiac(self):
|
||||
self.run_in_thread(generate_zodiac)
|
||||
|
||||
def run_export_data_folder(self):
|
||||
self.run_in_thread(export_data_folder)
|
||||
|
||||
def run_initialize_project(self):
|
||||
self.run_in_thread(initialize_project)
|
||||
|
||||
def quit_app(self):
|
||||
if messagebox.askokcancel("退出", "确定要退出系统吗?"):
|
||||
self.root.destroy()
|
||||
sys.exit()
|
||||
|
||||
|
||||
# ==========================================
|
||||
# 启动入口
|
||||
# ==========================================
|
||||
def applicationUI():
|
||||
# 【核心配置】在启动 GUI 前,配置 loguru
|
||||
# 1. remove() 移除默认的控制台输出(如果你想保留控制台黑窗口,可以注释掉这行)
|
||||
# logger.remove()
|
||||
|
||||
# 2. 添加我们的自定义 sink (queue_sink)
|
||||
# format 可以自定义,这里保持简单,或者尽量模拟 loguru 默认格式
|
||||
logger.add(
|
||||
queue_sink,
|
||||
format="{time:HH:mm:ss} | {level: <8} | {message}",
|
||||
level="INFO", # 只显示 INFO 及以上级别,避免 DEBUG 信息刷屏
|
||||
)
|
||||
|
||||
root = tk.Tk()
|
||||
app = ReportApp(root)
|
||||
root.mainloop()
|
||||
Reference in New Issue
Block a user