Files
yidaima_tools/gui.py

769 lines
32 KiB
Python
Raw Normal View History

2026-04-09 14:55:54 +08:00
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()