2025-09-04 12:55:33 +08:00
|
|
|
|
"""
|
|
|
|
|
|
作者:太一
|
|
|
|
|
|
微信:taiyi1224
|
|
|
|
|
|
邮箱:shuobo1224@qq.com
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2025-09-02 16:49:39 +08:00
|
|
|
|
import tkinter as tk
|
|
|
|
|
|
from tkinter import ttk, messagebox, filedialog
|
2025-09-04 12:55:33 +08:00
|
|
|
|
from tkinter import TclError
|
|
|
|
|
|
import math
|
2025-09-02 16:49:39 +08:00
|
|
|
|
from template_presets import TemplatePresetManager
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TemplateDialog(tk.Toplevel):
|
|
|
|
|
|
"""模板选择对话框"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, parent, current_template):
|
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
|
self.title("模板设置")
|
|
|
|
|
|
self.parent = parent
|
|
|
|
|
|
self.current_template = current_template
|
|
|
|
|
|
self.result = None
|
|
|
|
|
|
|
|
|
|
|
|
# 初始化预设管理器
|
|
|
|
|
|
self.preset_manager = TemplatePresetManager()
|
|
|
|
|
|
|
|
|
|
|
|
# 确保对话框模态
|
|
|
|
|
|
self.transient(parent)
|
|
|
|
|
|
self.grab_set()
|
|
|
|
|
|
|
|
|
|
|
|
# 设置布局
|
|
|
|
|
|
frame = ttk.Frame(self, padding=10)
|
|
|
|
|
|
frame.pack(fill=tk.BOTH, expand=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 预设管理
|
|
|
|
|
|
preset_frame = ttk.LabelFrame(frame, text="模板预设", padding=5)
|
|
|
|
|
|
preset_frame.grid(row=0, column=0, columnspan=3, sticky=tk.EW, pady=(0, 10))
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(preset_frame, text="选择预设:").grid(row=0, column=0, sticky=tk.W)
|
2025-09-04 12:55:33 +08:00
|
|
|
|
self.preset_var = tk.StringVar() # 默认空字符串,不选中任何预设
|
2025-09-02 16:49:39 +08:00
|
|
|
|
self.preset_combo = ttk.Combobox(preset_frame, textvariable=self.preset_var, width=30)
|
|
|
|
|
|
self.preset_combo.grid(row=0, column=1, padx=5)
|
|
|
|
|
|
self.update_preset_list()
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Button(preset_frame, text="加载", command=self.load_selected_preset).grid(row=0, column=2, padx=5)
|
|
|
|
|
|
ttk.Button(preset_frame, text="保存当前为预设", command=self.save_current_as_preset).grid(row=0, column=3, padx=5)
|
|
|
|
|
|
ttk.Button(preset_frame, text="删除选中预设", command=self.delete_selected_preset).grid(row=0, column=4, padx=5)
|
|
|
|
|
|
|
|
|
|
|
|
# 比例选择
|
|
|
|
|
|
ttk.Label(frame, text="选择比例:").grid(row=1, column=0, sticky=tk.W, pady=5)
|
|
|
|
|
|
self.ratio_var = tk.StringVar(value=f"{current_template.ratio[0]}:{current_template.ratio[1]}")
|
|
|
|
|
|
ratio_frame = ttk.Frame(frame)
|
|
|
|
|
|
ratio_frame.grid(row=1, column=1, sticky=tk.W, pady=5)
|
|
|
|
|
|
|
|
|
|
|
|
ratios = ["16:9", "3:4", "9:16", "4:3", "自定义"]
|
|
|
|
|
|
for i, ratio in enumerate(ratios):
|
|
|
|
|
|
ttk.Radiobutton(
|
|
|
|
|
|
ratio_frame,
|
|
|
|
|
|
text=ratio,
|
|
|
|
|
|
variable=self.ratio_var,
|
|
|
|
|
|
value=ratio,
|
|
|
|
|
|
command=self.on_ratio_change
|
|
|
|
|
|
).grid(row=0, column=i, padx=5)
|
|
|
|
|
|
|
|
|
|
|
|
# 自定义尺寸(默认隐藏)
|
|
|
|
|
|
self.custom_frame = ttk.Frame(frame)
|
|
|
|
|
|
ttk.Label(self.custom_frame, text="宽度:").grid(row=0, column=0, sticky=tk.W)
|
|
|
|
|
|
self.width_var = tk.IntVar(value=current_template.width_px)
|
2025-09-04 12:55:33 +08:00
|
|
|
|
width_entry = ttk.Entry(self.custom_frame, textvariable=self.width_var, width=8)
|
|
|
|
|
|
width_entry.grid(row=0, column=1, padx=5)
|
|
|
|
|
|
width_entry.bind("<FocusOut>", self.on_custom_size_change)
|
2025-09-02 16:49:39 +08:00
|
|
|
|
|
|
|
|
|
|
ttk.Label(self.custom_frame, text="高度:").grid(row=0, column=2, sticky=tk.W, padx=5)
|
|
|
|
|
|
self.height_var = tk.IntVar(value=current_template.height_px)
|
2025-09-04 12:55:33 +08:00
|
|
|
|
height_entry = ttk.Entry(self.custom_frame, textvariable=self.height_var, width=8)
|
|
|
|
|
|
height_entry.grid(row=0, column=3, padx=5)
|
|
|
|
|
|
height_entry.bind("<FocusOut>", self.on_custom_size_change)
|
2025-09-02 16:49:39 +08:00
|
|
|
|
|
|
|
|
|
|
# 背景设置
|
|
|
|
|
|
ttk.Label(frame, text="背景颜色:").grid(row=3, column=0, sticky=tk.W, pady=5)
|
|
|
|
|
|
self.bg_color_var = tk.StringVar(value=current_template.bg_color)
|
|
|
|
|
|
ttk.Entry(frame, textvariable=self.bg_color_var, width=10).grid(row=3, column=1, sticky=tk.W, pady=5)
|
|
|
|
|
|
|
|
|
|
|
|
ttk.Label(frame, text="背景图:").grid(row=4, column=0, sticky=tk.W, pady=5)
|
|
|
|
|
|
self.bg_image_var = tk.StringVar(value="无" if current_template.bg_image is None else "已设置")
|
|
|
|
|
|
ttk.Label(frame, textvariable=self.bg_image_var).grid(row=4, column=1, sticky=tk.W, pady=5)
|
|
|
|
|
|
|
|
|
|
|
|
btn_frame = ttk.Frame(frame)
|
|
|
|
|
|
ttk.Button(btn_frame, text="选择背景图", command=self.choose_bg_image).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
ttk.Button(btn_frame, text="清除背景图", command=self.clear_bg_image).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
btn_frame.grid(row=4, column=2, sticky=tk.W, pady=5)
|
|
|
|
|
|
|
|
|
|
|
|
# 确认和取消按钮
|
|
|
|
|
|
btn_frame = ttk.Frame(frame)
|
|
|
|
|
|
ttk.Button(btn_frame, text="确定", command=self.on_ok).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
ttk.Button(btn_frame, text="取消", command=self.on_cancel).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
btn_frame.grid(row=5, column=0, columnspan=3, pady=10)
|
|
|
|
|
|
|
|
|
|
|
|
# 检查当前是否为自定义比例
|
|
|
|
|
|
if f"{current_template.ratio[0]}:{current_template.ratio[1]}" not in ratios:
|
|
|
|
|
|
self.ratio_var.set("自定义")
|
|
|
|
|
|
self.show_custom_frame()
|
|
|
|
|
|
else:
|
|
|
|
|
|
self.hide_custom_frame()
|
|
|
|
|
|
|
|
|
|
|
|
# 调整大小
|
|
|
|
|
|
self.geometry("700x300")
|
|
|
|
|
|
self.wait_window(self)
|
|
|
|
|
|
|
|
|
|
|
|
def update_preset_list(self):
|
|
|
|
|
|
"""更新预设列表"""
|
|
|
|
|
|
presets = self.preset_manager.get_presets()
|
|
|
|
|
|
preset_names = [preset["name"] for preset in presets["presets"]]
|
|
|
|
|
|
self.preset_combo['values'] = preset_names
|
2025-09-04 12:55:33 +08:00
|
|
|
|
# 不自动选中任何预设,让用户手动选择
|
2025-09-02 16:49:39 +08:00
|
|
|
|
|
|
|
|
|
|
def load_selected_preset(self):
|
|
|
|
|
|
"""加载选中的预设"""
|
|
|
|
|
|
selected_preset = self.preset_var.get()
|
|
|
|
|
|
if not selected_preset:
|
|
|
|
|
|
messagebox.showwarning("警告", "请先选择一个预设")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2025-09-04 12:55:33 +08:00
|
|
|
|
# 设置标志以避免在刷新界面时更改尺寸
|
|
|
|
|
|
self._loading_preset = True
|
2025-09-02 16:49:39 +08:00
|
|
|
|
if self.preset_manager.load_preset_to_template(selected_preset, self.current_template):
|
|
|
|
|
|
# 更新界面显示
|
|
|
|
|
|
self.refresh_from_template()
|
|
|
|
|
|
# 刷新主窗口画布
|
|
|
|
|
|
self.parent.get_canvas().draw_preview()
|
|
|
|
|
|
messagebox.showinfo("成功", f"已加载预设: {selected_preset}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
messagebox.showerror("错误", "加载预设失败")
|
2025-09-04 12:55:33 +08:00
|
|
|
|
# 清除标志
|
|
|
|
|
|
self._loading_preset = False
|
2025-09-02 16:49:39 +08:00
|
|
|
|
|
|
|
|
|
|
def save_current_as_preset(self):
|
|
|
|
|
|
"""将当前设置保存为预设"""
|
|
|
|
|
|
# 弹出输入框获取预设名称
|
|
|
|
|
|
preset_name = tk.simpledialog.askstring("保存预设", "请输入预设名称:")
|
|
|
|
|
|
if not preset_name:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 保存当前模板为预设
|
|
|
|
|
|
if self.preset_manager.save_current_template_as_preset(self.current_template, preset_name):
|
|
|
|
|
|
# 更新预设列表
|
|
|
|
|
|
self.update_preset_list()
|
|
|
|
|
|
messagebox.showinfo("成功", f"预设已保存: {preset_name}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
messagebox.showerror("错误", "保存预设失败")
|
|
|
|
|
|
|
|
|
|
|
|
def delete_selected_preset(self):
|
|
|
|
|
|
"""删除选中的预设"""
|
|
|
|
|
|
selected_preset = self.preset_var.get()
|
|
|
|
|
|
if not selected_preset:
|
|
|
|
|
|
messagebox.showwarning("警告", "请先选择一个预设")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if messagebox.askyesno("确认", f"确定要删除预设 '{selected_preset}' 吗?"):
|
|
|
|
|
|
if self.preset_manager.remove_preset(selected_preset):
|
|
|
|
|
|
self.update_preset_list()
|
|
|
|
|
|
messagebox.showinfo("成功", f"预设已删除: {selected_preset}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
messagebox.showerror("错误", "删除预设失败")
|
|
|
|
|
|
|
|
|
|
|
|
def refresh_from_template(self):
|
|
|
|
|
|
"""根据当前模板刷新界面"""
|
|
|
|
|
|
# 更新比例选择
|
|
|
|
|
|
ratio_str = f"{self.current_template.ratio[0]}:{self.current_template.ratio[1]}"
|
|
|
|
|
|
self.ratio_var.set(ratio_str)
|
|
|
|
|
|
|
|
|
|
|
|
# 更新尺寸
|
|
|
|
|
|
self.width_var.set(self.current_template.width_px)
|
|
|
|
|
|
self.height_var.set(self.current_template.height_px)
|
|
|
|
|
|
|
|
|
|
|
|
# 更新背景颜色
|
|
|
|
|
|
self.bg_color_var.set(self.current_template.bg_color)
|
|
|
|
|
|
|
|
|
|
|
|
# 更新背景图状态
|
|
|
|
|
|
self.bg_image_var.set("无" if self.current_template.bg_image is None else "已设置")
|
|
|
|
|
|
|
|
|
|
|
|
# 处理自定义比例显示
|
|
|
|
|
|
ratios = ["16:9", "3:4", "9:16", "4:3"]
|
|
|
|
|
|
if ratio_str in ratios:
|
|
|
|
|
|
self.hide_custom_frame()
|
|
|
|
|
|
else:
|
|
|
|
|
|
self.show_custom_frame()
|
|
|
|
|
|
|
|
|
|
|
|
def on_ratio_change(self):
|
|
|
|
|
|
if self.ratio_var.get() == "自定义":
|
|
|
|
|
|
self.show_custom_frame()
|
2025-09-04 12:55:33 +08:00
|
|
|
|
# 更新比例显示为当前模板的比例
|
|
|
|
|
|
ratio_str = f"{self.current_template.ratio[0]}:{self.current_template.ratio[1]}"
|
|
|
|
|
|
self.ratio_var.set(ratio_str)
|
2025-09-02 16:49:39 +08:00
|
|
|
|
else:
|
|
|
|
|
|
self.hide_custom_frame()
|
2025-09-04 12:55:33 +08:00
|
|
|
|
if not (hasattr(self, '_loading_preset') and self._loading_preset):
|
|
|
|
|
|
try:
|
|
|
|
|
|
w, h = map(int, self.ratio_var.get().split(":"))
|
|
|
|
|
|
current_width = self.width_var.get()
|
|
|
|
|
|
if current_width <= 0:
|
|
|
|
|
|
current_width = 100 # 避免宽度为0
|
|
|
|
|
|
new_height = int(current_width * h / w)
|
|
|
|
|
|
new_height = max(10, new_height) # 确保最小高度
|
|
|
|
|
|
self.height_var.set(new_height)
|
|
|
|
|
|
# 同步更新当前模板的尺寸
|
|
|
|
|
|
self.current_template.width_px = current_width
|
|
|
|
|
|
self.current_template.height_px = new_height
|
|
|
|
|
|
self.current_template.ratio = (w, h)
|
|
|
|
|
|
except (ValueError, ZeroDivisionError):
|
|
|
|
|
|
messagebox.showerror("错误", "无效的比例格式")
|
2025-09-02 16:49:39 +08:00
|
|
|
|
|
|
|
|
|
|
def show_custom_frame(self):
|
|
|
|
|
|
"""显示自定义尺寸框"""
|
|
|
|
|
|
self.custom_frame.grid(row=2, column=0, columnspan=3, sticky=tk.W, pady=5)
|
|
|
|
|
|
|
|
|
|
|
|
def hide_custom_frame(self):
|
|
|
|
|
|
"""隐藏自定义尺寸框"""
|
|
|
|
|
|
self.custom_frame.grid_forget()
|
|
|
|
|
|
|
2025-09-04 12:55:33 +08:00
|
|
|
|
def on_custom_size_change(self, event=None):
|
|
|
|
|
|
"""自定义尺寸变化时的处理"""
|
|
|
|
|
|
if self.ratio_var.get() == "自定义":
|
|
|
|
|
|
try:
|
|
|
|
|
|
width = self.width_var.get()
|
|
|
|
|
|
height = self.height_var.get()
|
|
|
|
|
|
if width <= 0 or height <= 0:
|
|
|
|
|
|
return
|
|
|
|
|
|
# 更新当前模板的尺寸
|
|
|
|
|
|
self.current_template.set_custom_size(width, height)
|
|
|
|
|
|
# 更新比例显示
|
|
|
|
|
|
ratio_str = f"{self.current_template.ratio[0]}:{self.current_template.ratio[1]}"
|
|
|
|
|
|
self.ratio_var.set(ratio_str)
|
|
|
|
|
|
# 刷新主窗口画布
|
|
|
|
|
|
self.parent.get_canvas().draw_preview()
|
|
|
|
|
|
except (ValueError, TclError):
|
|
|
|
|
|
# 忽略无效输入
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
2025-09-02 16:49:39 +08:00
|
|
|
|
def choose_bg_image(self):
|
|
|
|
|
|
"""选择背景图"""
|
|
|
|
|
|
file_path = filedialog.askopenfilename(
|
2025-09-04 12:55:33 +08:00
|
|
|
|
title="选择背景图",
|
|
|
|
|
|
filetypes=[("图片文件", "*.png *.jpg *.jpeg *.gif *.bmp")]
|
2025-09-02 16:49:39 +08:00
|
|
|
|
)
|
|
|
|
|
|
if file_path:
|
|
|
|
|
|
if self.current_template.load_background(file_path):
|
|
|
|
|
|
self.bg_image_var.set("已设置")
|
2025-09-04 12:55:33 +08:00
|
|
|
|
# 刷新主窗口画布
|
|
|
|
|
|
self.parent.get_canvas().draw_preview()
|
|
|
|
|
|
else:
|
|
|
|
|
|
messagebox.showerror("错误", "加载背景图失败")
|
2025-09-02 16:49:39 +08:00
|
|
|
|
|
|
|
|
|
|
def clear_bg_image(self):
|
|
|
|
|
|
"""清除背景图"""
|
|
|
|
|
|
self.current_template.clear_background()
|
|
|
|
|
|
self.bg_image_var.set("无")
|
2025-09-04 12:55:33 +08:00
|
|
|
|
# 刷新主窗口画布
|
|
|
|
|
|
self.parent.get_canvas().draw_preview()
|
2025-09-02 16:49:39 +08:00
|
|
|
|
|
|
|
|
|
|
def on_ok(self):
|
|
|
|
|
|
"""确认按钮"""
|
2025-09-04 12:55:33 +08:00
|
|
|
|
# 只有在用户主动点击"加载"按钮加载预设时才应用预设
|
|
|
|
|
|
# 不再自动应用选中的预设
|
2025-09-02 16:49:39 +08:00
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
if self.ratio_var.get() == "自定义":
|
2025-09-04 12:55:33 +08:00
|
|
|
|
try:
|
|
|
|
|
|
width = self.width_var.get()
|
|
|
|
|
|
height = self.height_var.get()
|
|
|
|
|
|
except tk.TclError:
|
|
|
|
|
|
messagebox.showerror("错误", "请输入有效的整数")
|
|
|
|
|
|
return
|
2025-09-02 16:49:39 +08:00
|
|
|
|
if width <= 0 or height <= 0:
|
|
|
|
|
|
messagebox.showerror("错误", "宽度和高度必须为正数")
|
|
|
|
|
|
return
|
2025-09-04 12:55:33 +08:00
|
|
|
|
# 更新当前模板
|
|
|
|
|
|
self.current_template.set_custom_size(width, height)
|
|
|
|
|
|
# 显式设置ratio属性为自定义比例
|
|
|
|
|
|
gcd = math.gcd(width, height)
|
|
|
|
|
|
ratio_w = width // gcd
|
|
|
|
|
|
ratio_h = height // gcd
|
|
|
|
|
|
self.current_template.ratio = (ratio_w, ratio_h)
|
|
|
|
|
|
self.current_template.bg_color = self.bg_color_var.get()
|
|
|
|
|
|
|
2025-09-02 16:49:39 +08:00
|
|
|
|
self.result = {
|
|
|
|
|
|
"type": "custom",
|
|
|
|
|
|
"width": width,
|
|
|
|
|
|
"height": height,
|
2025-09-04 12:55:33 +08:00
|
|
|
|
"ratio": (ratio_w, ratio_h),
|
|
|
|
|
|
"bg_color": self.bg_color_var.get(),
|
|
|
|
|
|
"bg_image": self.current_template.bg_image,
|
|
|
|
|
|
"bg_image_path": self.current_template.bg_image_path
|
2025-09-02 16:49:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
else:
|
|
|
|
|
|
w, h = map(int, self.ratio_var.get().split(":"))
|
2025-09-04 12:55:33 +08:00
|
|
|
|
# 更新当前模板
|
|
|
|
|
|
self.current_template.ratio = (w, h)
|
|
|
|
|
|
self.current_template.width_px = self.width_var.get()
|
|
|
|
|
|
self.current_template.height_px = self.height_var.get()
|
|
|
|
|
|
self.current_template.bg_color = self.bg_color_var.get()
|
|
|
|
|
|
|
2025-09-02 16:49:39 +08:00
|
|
|
|
self.result = {
|
|
|
|
|
|
"type": "preset",
|
2025-09-04 12:55:33 +08:00
|
|
|
|
"ratio": tuple([w, h]),
|
2025-09-02 16:49:39 +08:00
|
|
|
|
"width": self.width_var.get(),
|
2025-09-04 12:55:33 +08:00
|
|
|
|
"height": self.height_var.get(),
|
|
|
|
|
|
"bg_color": self.bg_color_var.get(),
|
|
|
|
|
|
"bg_image": self.current_template.bg_image,
|
|
|
|
|
|
"bg_image_path": self.current_template.bg_image_path
|
2025-09-02 16:49:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
self.destroy()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
messagebox.showerror("错误", f"设置模板失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
def on_cancel(self):
|
|
|
|
|
|
"""取消按钮"""
|
|
|
|
|
|
self.result = None
|
|
|
|
|
|
self.destroy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-09-04 12:55:33 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-09-02 16:49:39 +08:00
|
|
|
|
|
|
|
|
|
|
|