fix:添加UI界面,完善功能

This commit is contained in:
2025-12-11 11:16:09 +08:00
parent 81e3c40abb
commit f437842a81
17 changed files with 939 additions and 406 deletions

199
UI.py Normal file
View 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()