Files
yidaima_tools/gui.py
王鹏 a2f5875d1b init
2026-04-09 14:55:54 +08:00

769 lines
32 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import threading
import sys
import os
# 添加当前目录到路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from config_loader import get_config, Config
from step1 import Step1FeastCoding
from step2 import Step2Converter
from editor_gui import open_editor
from markdown_editor import ThemeManager
from db_manager import get_db_manager, reset_db_manager, ProjectOrder
class YidaimaGUI:
def __init__(self, root):
self.root = root
self.root.title("自动化工具")
self.root.geometry("1200x900")
self.root.resizable(True, True)
# 设置样式
self.style = ttk.Style()
self.style.configure("TButton", padding=6, relief="flat", background="#007aff")
self.style.configure("TLabel", padding=6, font=("Microsoft YaHei", 10))
self.style.configure("TEntry", padding=6)
# 设置 Treeview 样式,增加行高
self.style.configure("Treeview", rowheight=30)
# 加载配置
self.config = get_config()
# 初始化管理器
self.theme_manager = ThemeManager()
self._init_db_manager()
# 运行状态
self.is_running = False
# 文章管理分页状态
self.current_page = 1
self.page_size = 20
self.total_items = 0
self.search_keyword = ""
# 创建界面
self.create_widgets()
def _init_db_manager(self):
"""初始化数据库管理器"""
try:
db_config = self.config.get("database", {})
self.db_manager = get_db_manager(
host=db_config.get("host", "localhost"),
database=db_config.get("database", "test"),
user=db_config.get("user", "root"),
password=db_config.get("password", "123456"),
port=db_config.get("port", 3306)
)
except Exception as e:
print(f"[GUI] 初始化数据库管理器失败: {e}")
self.db_manager = None
def create_widgets(self):
"""创建主界面"""
# 主框架
main_frame = ttk.Frame(self.root, padding="10")
main_frame.pack(fill=tk.BOTH, expand=True)
# 创建 Notebook (Tab 控件)
self.notebook = ttk.Notebook(main_frame)
self.notebook.pack(fill=tk.BOTH, expand=True)
# === Tab 1: 发布微信公众号 ===
self.tab_publish = ttk.Frame(self.notebook, padding="10")
self.notebook.add(self.tab_publish, text="发布微信公众号")
self._create_publish_tab()
# === Tab 2: 文章发布管理 ===
self.tab_manage = ttk.Frame(self.notebook, padding="10")
self.notebook.add(self.tab_manage, text="文章发布管理")
self._create_manage_tab()
# === Tab 4: 参数设置 ===
self.tab_settings = ttk.Frame(self.notebook, padding="10")
self.notebook.add(self.tab_settings, text="参数设置")
self._create_settings_tab()
def _create_publish_tab(self):
"""创建发布微信公众号 Tab"""
# 配置网格
self.tab_publish.columnconfigure(0, weight=1)
self.tab_publish.rowconfigure(2, weight=1)
# === 配置区域 ===
config_frame = ttk.LabelFrame(self.tab_publish, text="微信公众号配置", padding="10")
config_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
config_frame.columnconfigure(1, weight=1)
# 项目名称
ttk.Label(config_frame, text="项目名称:").grid(row=0, column=0, sticky=tk.W)
self.project_name_var = tk.StringVar(
value=self.config.get("step1.project_name", "【A173】基于Springboot + vue3实现的学生交流互助平台")
)
self.project_name_entry = ttk.Entry(config_frame, textvariable=self.project_name_var, width=50)
self.project_name_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0))
# CSS 样式方案
ttk.Label(config_frame, text="CSS 样式方案:").grid(row=1, column=0, sticky=tk.W, pady=(10, 0))
self.css_scheme_var = tk.StringVar()
scheme_frame = ttk.Frame(config_frame)
scheme_frame.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
scheme_frame.columnconfigure(0, weight=1)
self.css_scheme_combo = ttk.Combobox(
scheme_frame,
values=self._get_css_scheme_names(),
textvariable=self.css_scheme_var,
state="readonly",
width=50
)
self.css_scheme_combo.grid(row=0, column=0, sticky=(tk.W, tk.E))
self.css_scheme_combo.set("默认")
# 刷新按钮
ttk.Button(scheme_frame, text="刷新", command=self.refresh_css_schemes, width=8).grid(row=0, column=1, padx=(5, 0))
# === 操作按钮区域 ===
button_frame = ttk.Frame(self.tab_publish)
button_frame.grid(row=1, column=0, pady=(10, 10))
# 发布微信公众号按钮
self.publish_btn = ttk.Button(
button_frame,
text="发布微信公众号",
command=self.run_full_flow,
width=25
)
self.publish_btn.pack(side=tk.LEFT)
# Markdown 编辑器按钮
ttk.Button(
button_frame,
text="Markdown 编辑器",
command=self._open_markdown_editor,
width=15
).pack(side=tk.LEFT, padx=(10, 0))
# === 日志输出区域 ===
log_frame = ttk.LabelFrame(self.tab_publish, text="运行日志", padding="10")
log_frame.grid(row=2, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(10, 0))
log_frame.columnconfigure(0, weight=1)
log_frame.rowconfigure(0, weight=1)
self.log_text = scrolledtext.ScrolledText(
log_frame,
wrap=tk.WORD,
width=80,
height=15,
font=("Consolas", 9)
)
self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 进度条
self.progress = ttk.Progressbar(
self.tab_publish,
mode='indeterminate',
length=400
)
self.progress.grid(row=3, column=0, pady=(10, 0), sticky=(tk.W, tk.E))
# 状态标签
self.status_var = tk.StringVar(value="就绪")
self.status_label = ttk.Label(
self.tab_publish,
textvariable=self.status_var,
foreground="#666"
)
self.status_label.grid(row=4, column=0, pady=(10, 0))
def _create_manage_tab(self):
"""创建文章发布管理 Tab"""
# 配置网格
self.tab_manage.columnconfigure(0, weight=1)
self.tab_manage.rowconfigure(2, weight=1)
# === 搜索区域 ===
search_frame = ttk.Frame(self.tab_manage)
search_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
search_frame.columnconfigure(1, weight=1)
ttk.Label(search_frame, text="搜索名称:").grid(row=0, column=0, sticky=tk.W)
self.search_var = tk.StringVar()
self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=40)
self.search_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0))
ttk.Button(search_frame, text="搜索", command=self._search_projects, width=10).grid(row=0, column=2, padx=(10, 0))
ttk.Button(search_frame, text="刷新", command=self._refresh_projects, width=10).grid(row=0, column=3, padx=(10, 0))
# === 数据表格区域 ===
table_frame = ttk.LabelFrame(self.tab_manage, text="项目列表", padding="5")
table_frame.grid(row=1, column=0, rowspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
table_frame.columnconfigure(0, weight=1)
table_frame.rowconfigure(0, weight=1)
# 创建 Treeview
columns = ("name", "name2", "paths", "zlzt", "sfxxm", "bianhao", "actions")
self.tree = ttk.Treeview(
table_frame,
columns=columns,
show="headings",
height=35
)
# 设置列标题
self.tree.heading("name", text="项目名称")
self.tree.heading("name2", text="名称2")
self.tree.heading("paths", text="路径")
self.tree.heading("zlzt", text="整理状态")
self.tree.heading("sfxxm", text="是否新项目")
self.tree.heading("bianhao", text="编号")
self.tree.heading("actions", text="操作")
# 设置列宽
self.tree.column("name", width=150, anchor=tk.W)
self.tree.column("name2", width=100, anchor=tk.W)
self.tree.column("paths", width=120, anchor=tk.W)
self.tree.column("zlzt", width=80, anchor=tk.CENTER)
self.tree.column("sfxxm", width=80, anchor=tk.CENTER)
self.tree.column("bianhao", width=80, anchor=tk.CENTER)
self.tree.column("actions", width=200, anchor=tk.CENTER)
# 添加滚动条
scrollbar_y = ttk.Scrollbar(table_frame, orient=tk.VERTICAL, command=self.tree.yview)
scrollbar_x = ttk.Scrollbar(table_frame, orient=tk.HORIZONTAL, command=self.tree.xview)
self.tree.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
self.tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
scrollbar_y.grid(row=0, column=1, sticky=(tk.N, tk.S))
scrollbar_x.grid(row=1, column=0, sticky=(tk.W, tk.E))
# 绑定右键点击事件
self.tree.bind("<Button-3>", self._on_tree_right_click)
# === 分页区域 ===
page_frame = ttk.Frame(self.tab_manage)
page_frame.grid(row=3, column=0, sticky=(tk.W, tk.E), pady=(5, 0))
self.page_info_var = tk.StringVar(value="第 1 页,共 0 页,共 0 条")
ttk.Label(page_frame, textvariable=self.page_info_var).pack(side=tk.LEFT)
ttk.Button(page_frame, text="上一页", command=self._prev_page).pack(side=tk.LEFT, padx=(20, 5))
ttk.Button(page_frame, text="下一页", command=self._next_page).pack(side=tk.LEFT, padx=(5, 0))
# 加载数据(延迟加载,避免初始化时出错)
# self._load_projects()
def _create_settings_tab(self):
"""创建参数设置 Tab"""
# 创建 Canvas 和 Scrollbar 实现滚动
canvas = tk.Canvas(self.tab_settings)
scrollbar = ttk.Scrollbar(self.tab_settings, orient="vertical", command=canvas.yview)
self.settings_frame = ttk.Frame(canvas, padding="10")
self.settings_frame.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.create_window((0, 0), window=self.settings_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
# 配置网格
self.settings_frame.columnconfigure(1, weight=1)
row = 0
# === 网站账号配置 ===
website_frame = ttk.LabelFrame(self.settings_frame, text="网站账号配置", padding="10")
website_frame.grid(row=row, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 15))
website_frame.columnconfigure(1, weight=1)
ttk.Label(website_frame, text="网站用户名:").grid(row=0, column=0, sticky=tk.W)
self.setting_username_var = tk.StringVar(value=self.config.get("step1.username", "wangpeng"))
ttk.Entry(website_frame, textvariable=self.setting_username_var, width=40).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0))
ttk.Label(website_frame, text="网站密码:").grid(row=1, column=0, sticky=tk.W, pady=(10, 0))
self.setting_password_var = tk.StringVar(value=self.config.get("step1.password", ""))
ttk.Entry(website_frame, textvariable=self.setting_password_var, show="*", width=40).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
row += 1
# === Chrome 配置 ===
chrome_frame = ttk.LabelFrame(self.settings_frame, text="Chrome 配置", padding="10")
chrome_frame.grid(row=row, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 15))
chrome_frame.columnconfigure(1, weight=1)
ttk.Label(chrome_frame, text="Chrome 路径:").grid(row=0, column=0, sticky=tk.W)
self.setting_chrome_var = tk.StringVar(value=self.config.get("chrome.path", r"C:\Program Files\Google\Chrome\Application\chrome.exe"))
ttk.Entry(chrome_frame, textvariable=self.setting_chrome_var, width=50).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0))
row += 1
# === 微信公众号配置 ===
wechat_frame = ttk.LabelFrame(self.settings_frame, text="微信公众号配置", padding="10")
wechat_frame.grid(row=row, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 15))
wechat_frame.columnconfigure(1, weight=1)
ttk.Label(wechat_frame, text="AppID:").grid(row=0, column=0, sticky=tk.W)
self.setting_appid_var = tk.StringVar(value=self.config.get("wechat.appid", ""))
ttk.Entry(wechat_frame, textvariable=self.setting_appid_var, width=40).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0))
ttk.Label(wechat_frame, text="AppSecret:").grid(row=1, column=0, sticky=tk.W, pady=(10, 0))
self.setting_appsecret_var = tk.StringVar(value=self.config.get("wechat.appsecret", ""))
ttk.Entry(wechat_frame, textvariable=self.setting_appsecret_var, show="*", width=40).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
row += 1
# === 数据库配置 ===
db_frame = ttk.LabelFrame(self.settings_frame, text="数据库配置", padding="10")
db_frame.grid(row=row, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 15))
db_frame.columnconfigure(1, weight=1)
ttk.Label(db_frame, text="主机地址:").grid(row=0, column=0, sticky=tk.W)
self.setting_db_host_var = tk.StringVar(value=self.config.get("database.host", "localhost"))
ttk.Entry(db_frame, textvariable=self.setting_db_host_var, width=30).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0))
ttk.Label(db_frame, text="端口:").grid(row=1, column=0, sticky=tk.W, pady=(10, 0))
self.setting_db_port_var = tk.StringVar(value=str(self.config.get("database.port", "3306")))
ttk.Entry(db_frame, textvariable=self.setting_db_port_var, width=30).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
ttk.Label(db_frame, text="数据库名:").grid(row=2, column=0, sticky=tk.W, pady=(10, 0))
self.setting_db_name_var = tk.StringVar(value=self.config.get("database.database", "test"))
ttk.Entry(db_frame, textvariable=self.setting_db_name_var, width=30).grid(row=2, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
ttk.Label(db_frame, text="用户名:").grid(row=3, column=0, sticky=tk.W, pady=(10, 0))
self.setting_db_user_var = tk.StringVar(value=self.config.get("database.user", "root"))
ttk.Entry(db_frame, textvariable=self.setting_db_user_var, width=30).grid(row=3, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
ttk.Label(db_frame, text="密码:").grid(row=4, column=0, sticky=tk.W, pady=(10, 0))
self.setting_db_pass_var = tk.StringVar(value=self.config.get("database.password", "123456"))
ttk.Entry(db_frame, textvariable=self.setting_db_pass_var, show="*", width=30).grid(row=4, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
# 测试连接按钮
ttk.Button(db_frame, text="🧪 测试连接", command=self._test_db_connection).grid(row=5, column=1, sticky=tk.W, pady=(10, 0))
row += 1
# === 保存按钮 ===
btn_frame = ttk.Frame(self.settings_frame)
btn_frame.grid(row=row, column=0, columnspan=2, pady=(20, 0))
ttk.Button(btn_frame, text="💾 保存配置", command=self._save_settings, width=20).pack(side=tk.LEFT, padx=(0, 10))
ttk.Button(btn_frame, text="🔄 重置", command=self._reset_settings, width=15).pack(side=tk.LEFT)
# ==================== 发布公众号 Tab 方法 ====================
def _open_markdown_editor(self):
"""打开 Markdown 编辑器"""
try:
open_editor(parent=self.root)
except Exception as e:
messagebox.showerror("错误", f"打开 Markdown 编辑器失败: {e}")
def log(self, message: str):
"""添加日志"""
self.log_text.insert(tk.END, f"{message}\n")
self.log_text.see(tk.END)
self.root.update_idletasks()
def set_running(self, running: bool):
"""设置运行状态"""
self.is_running = running
state = tk.DISABLED if running else tk.NORMAL
self.publish_btn.config(state=state)
if running:
self.progress.start()
self.status_var.set("运行中...")
else:
self.progress.stop()
self.status_var.set("就绪")
def run_full_flow(self):
"""运行完整流程"""
if self.is_running:
return
thread = threading.Thread(target=self._run_full_flow_thread)
thread.daemon = True
thread.start()
def _run_full_flow_thread(self):
"""在后台线程中运行完整流程"""
try:
self.set_running(True)
self.root.after(0, lambda: self.log("=" * 50))
self.root.after(0, lambda: self.log("开始运行完整流程..."))
self.root.after(0, lambda: self.log("=" * 50))
# 获取配置(从配置文件读取)
chrome_path = self.config.get("chrome.path", "")
username = self.config.get("step1.username", "")
password = self.config.get("step1.password", "")
project_name = self.project_name_var.get()
# Step 1
self.root.after(0, lambda: self.log("\n[Step 1] 开始执行..."))
# 定义日志回调函数,将 step1 的日志输出到 GUI
def step1_log_callback(message: str):
self.root.after(0, lambda msg=message: self.log(msg))
step1 = Step1FeastCoding(chrome_path, username, password, log_callback=step1_log_callback)
step1.project_name = project_name # 设置项目名称
step1.run()
self.root.after(0, lambda: self.log("[Step 1] 执行完成!"))
# Step 2
self.root.after(0, lambda: self.log("\n[Step 2] 开始执行..."))
# 定义日志回调函数,将 step2 的日志输出到 GUI
def log_callback(message: str):
self.root.after(0, lambda msg=message: self.log(msg))
# 获取 CSS 方案 ID根据名称查找
scheme_name = self.css_scheme_var.get()
css_scheme_id = self._get_scheme_id_by_name(scheme_name)
step2 = Step2Converter(log_callback=log_callback, css_scheme_id=css_scheme_id)
step2.run()
self.root.after(0, lambda: self.log("[Step 2] 执行完成!"))
self.root.after(0, lambda: self.log("\n" + "=" * 50))
self.root.after(0, lambda: self.log("完整流程执行成功!"))
self.root.after(0, lambda: self.log("=" * 50))
self.root.after(0, lambda: messagebox.showinfo("成功", "完整流程执行成功!"))
except Exception as e:
error_msg = str(e)
self.root.after(0, lambda: self.log(f"\n[ERROR] {error_msg}"))
self.root.after(0, lambda: messagebox.showerror("错误", f"执行失败: {error_msg}"))
finally:
self.set_running(False)
def _get_css_scheme_names(self):
"""获取 CSS 方案名称列表(只显示名称)"""
schemes = self.theme_manager.get_all_css_schemes()
return ["默认"] + [s.name for s in schemes]
def _get_scheme_id_by_name(self, name: str):
"""根据名称获取方案 ID"""
if name == "默认":
return None
schemes = self.theme_manager.get_all_css_schemes()
for scheme in schemes:
if scheme.name == name:
return scheme.id
return None
def refresh_css_schemes(self):
"""刷新 CSS 方案列表"""
# 重新加载配置
self.theme_manager = ThemeManager()
# 更新下拉框
self.css_scheme_combo['values'] = self._get_css_scheme_names()
messagebox.showinfo("提示", "CSS 方案列表已刷新!")
# ==================== 文章管理 Tab 方法 ====================
def _load_projects(self):
"""加载项目列表"""
if not self.db_manager:
messagebox.showwarning("警告", "数据库未配置或连接失败")
return
try:
projects, total = self.db_manager.get_projects(
page=self.current_page,
page_size=self.page_size,
search_name=self.search_keyword if self.search_keyword else None
)
self.total_items = total
# 清空表格
for item in self.tree.get_children():
self.tree.delete(item)
# 插入数据
for project in projects:
self.tree.insert("", tk.END, values=(
project.name or "",
project.name2 or "",
project.paths or "",
project.zlzt or "",
project.sfxxm or "",
project.bianhao or "",
"右键操作"
))
# 更新分页信息
total_pages = (total + self.page_size - 1) // self.page_size if total > 0 else 1
self.page_info_var.set(f"{self.current_page} 页,共 {total_pages} 页,共 {total}")
except Exception as e:
messagebox.showerror("错误", f"加载数据失败: {e}")
def _search_projects(self):
"""搜索项目"""
try:
self.search_keyword = self.search_var.get().strip()
self.current_page = 1
self._load_projects()
except Exception as e:
messagebox.showerror("错误", f"搜索失败: {e}")
def _refresh_projects(self):
"""刷新项目列表"""
self.search_keyword = ""
self.search_var.set("")
self.current_page = 1
self._load_projects()
def _prev_page(self):
"""上一页"""
if self.current_page > 1:
self.current_page -= 1
self._load_projects()
def _next_page(self):
"""下一页"""
total_pages = (self.total_items + self.page_size - 1) // self.page_size if self.total_items > 0 else 1
if self.current_page < total_pages:
self.current_page += 1
self._load_projects()
def _on_tree_right_click(self, event):
"""处理表格右键点击事件"""
# 获取点击位置对应的项
item = self.tree.identify_row(event.y)
if not item:
return
# 选中当前项
self.tree.selection_set(item)
# 获取选中项的值
values = self.tree.item(item, "values")
if not values:
return
name = values[0]
name2 = values[1]
current_zlzt = values[3] # 整理状态现在在第4列索引3
# 创建操作菜单
menu = tk.Menu(self.root, tearoff=0)
# 变更已整理状态选项
new_zlzt = "已整理" if current_zlzt != "已整理" else "未整理"
menu.add_command(
label=f"变更状态: {new_zlzt}",
command=lambda: self._update_project_zlzt(name, new_zlzt)
)
menu.add_separator()
# 删除选项
menu.add_command(
label="删除",
command=lambda: self._delete_project(name)
)
menu.add_separator()
# 复制路径选项
menu.add_command(
label="复制路径",
command=lambda: self._copy_path(name)
)
menu.add_separator()
# 复制name2名称选项
menu.add_command(
label="复制名称2",
command=lambda: self._copy_name2(name2)
)
# 显示菜单
menu.post(event.x_root, event.y_root)
def _update_project_zlzt(self, name: str, zlzt: str):
"""更新项目整理状态"""
if not self.db_manager:
return
if messagebox.askyesno("确认", f"确定将 '{name}' 标记为 {zlzt} 吗?"):
if self.db_manager.update_zlzt(name, zlzt):
messagebox.showinfo("成功", f"已更新为 {zlzt}")
self._load_projects()
else:
messagebox.showerror("错误", "更新失败")
def _delete_project(self, name: str):
"""删除项目"""
if not self.db_manager:
return
if messagebox.askyesno("确认", f"确定删除 '{name}' 吗?\n此操作不可恢复!"):
if self.db_manager.delete_project(name):
messagebox.showinfo("成功", "删除成功")
self._load_projects()
else:
messagebox.showerror("错误", "删除失败")
def _copy_name2(self, name2: str):
"""复制name2名称到剪贴板"""
if not name2:
messagebox.showwarning("提示", "名称2为空无法复制")
return
self.root.clipboard_clear()
self.root.clipboard_append(name2)
messagebox.showinfo("成功", f"已复制: {name2}")
def _copy_path(self, name: str):
"""复制项目路径到剪贴板"""
if not self.db_manager:
messagebox.showwarning("提示", "数据库未连接,无法获取路径")
return
try:
# 获取项目路径
project = self.db_manager.get_project_by_name(name)
if project and project.paths:
self.root.clipboard_clear()
self.root.clipboard_append(project.paths)
messagebox.showinfo("成功", f"已复制路径: {project.paths}")
else:
messagebox.showwarning("提示", "项目路径为空,无法复制")
except Exception as e:
messagebox.showerror("错误", f"复制路径失败: {e}")
# ==================== 参数设置 Tab 方法 ====================
def _test_db_connection(self):
"""测试数据库连接"""
def test_in_thread():
try:
from db_manager import DatabaseManager
host = self.setting_db_host_var.get()
port_str = self.setting_db_port_var.get()
port = int(port_str) if port_str else 3306
database = self.setting_db_name_var.get()
user = self.setting_db_user_var.get()
password = self.setting_db_pass_var.get()
print(f"[TestDB] 测试连接: {host}:{port}/{database} as {user}")
db = DatabaseManager(host, database, user, password, port)
success, error = db.test_connection()
if success:
self.root.after(0, lambda: messagebox.showinfo("成功", "数据库连接成功!"))
else:
self.root.after(0, lambda: messagebox.showerror("失败", f"连接失败: {error}"))
except Exception as e:
print(f"[TestDB] 测试连接异常: {e}")
self.root.after(0, lambda msg=str(e): messagebox.showerror("错误", f"测试连接出错: {msg}"))
# 在新线程中运行,避免阻塞 GUI
thread = threading.Thread(target=test_in_thread)
thread.daemon = True
thread.start()
def _save_settings(self):
"""保存配置"""
try:
# 构建新配置
new_config = {
"step1": {
"url": "https://feast.yidaima.cn/login",
"username": self.setting_username_var.get(),
"password": self.setting_password_var.get(),
"project_name": self.project_name_var.get(),
"feast_button": "FeastCoding"
},
"step2": {
"url": "http://yidaima.cn:6005/"
},
"chrome": {
"path": self.setting_chrome_var.get()
},
"wechat": {
"appid": self.setting_appid_var.get(),
"appsecret": self.setting_appsecret_var.get()
},
"database": {
"host": self.setting_db_host_var.get(),
"port": int(self.setting_db_port_var.get() or 3306),
"database": self.setting_db_name_var.get(),
"user": self.setting_db_user_var.get(),
"password": self.setting_db_pass_var.get()
}
}
# 保存到文件
import yaml
config_path = os.path.join(os.path.dirname(__file__), "config.yaml")
with open(config_path, 'w', encoding='utf-8') as f:
yaml.dump(new_config, f, allow_unicode=True, sort_keys=False)
# 重新加载配置
global _config_instance
_config_instance = None
self.config = get_config()
# 重置数据库管理器
reset_db_manager()
self._init_db_manager()
# 更新发布页面的值
self.username_var.set(self.config.get("step1.username", ""))
self.password_var.set(self.config.get("step1.password", ""))
self.chrome_path_var.set(self.config.get("chrome.path", ""))
messagebox.showinfo("成功", "配置已保存!")
except Exception as e:
messagebox.showerror("错误", f"保存失败: {e}")
def _reset_settings(self):
"""重置设置"""
if messagebox.askyesno("确认", "确定要重置所有设置吗?"):
self.setting_username_var.set(self.config.get("step1.username", "wangpeng"))
self.setting_password_var.set(self.config.get("step1.password", ""))
self.setting_chrome_var.set(self.config.get("chrome.path", r"C:\Program Files\Google\Chrome\Application\chrome.exe"))
self.setting_appid_var.set(self.config.get("wechat.appid", ""))
self.setting_appsecret_var.set(self.config.get("wechat.appsecret", ""))
self.setting_db_host_var.set(self.config.get("database.host", "localhost"))
self.setting_db_port_var.set(str(self.config.get("database.port", "3306")))
self.setting_db_name_var.set(self.config.get("database.database", "test"))
self.setting_db_user_var.set(self.config.get("database.user", "root"))
self.setting_db_pass_var.set(self.config.get("database.password", "123456"))
def main():
"""启动 GUI"""
root = tk.Tk()
app = YidaimaGUI(root)
root.mainloop()
if __name__ == "__main__":
main()