PictureEdit/template_dialog.py
2025-09-04 12:58:13 +08:00

345 lines
14 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.

"""
作者:太一
微信taiyi1224
邮箱shuobo1224@qq.com
"""
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from tkinter import TclError
import math
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)
self.preset_var = tk.StringVar() # 默认空字符串,不选中任何预设
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)
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)
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)
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)
# 背景设置
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
# 不自动选中任何预设,让用户手动选择
def load_selected_preset(self):
"""加载选中的预设"""
selected_preset = self.preset_var.get()
if not selected_preset:
messagebox.showwarning("警告", "请先选择一个预设")
return
# 设置标志以避免在刷新界面时更改尺寸
self._loading_preset = True
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("错误", "加载预设失败")
# 清除标志
self._loading_preset = False
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()
# 更新比例显示为当前模板的比例
ratio_str = f"{self.current_template.ratio[0]}:{self.current_template.ratio[1]}"
self.ratio_var.set(ratio_str)
else:
self.hide_custom_frame()
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("错误", "无效的比例格式")
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()
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
def choose_bg_image(self):
"""选择背景图"""
file_path = filedialog.askopenfilename(
title="选择背景图",
filetypes=[("图片文件", "*.png *.jpg *.jpeg *.gif *.bmp")]
)
if file_path:
if self.current_template.load_background(file_path):
self.bg_image_var.set("已设置")
# 刷新主窗口画布
self.parent.get_canvas().draw_preview()
else:
messagebox.showerror("错误", "加载背景图失败")
def clear_bg_image(self):
"""清除背景图"""
self.current_template.clear_background()
self.bg_image_var.set("")
# 刷新主窗口画布
self.parent.get_canvas().draw_preview()
def on_ok(self):
"""确认按钮"""
# 只有在用户主动点击"加载"按钮加载预设时才应用预设
# 不再自动应用选中的预设
try:
if self.ratio_var.get() == "自定义":
try:
width = self.width_var.get()
height = self.height_var.get()
except tk.TclError:
messagebox.showerror("错误", "请输入有效的整数")
return
if width <= 0 or height <= 0:
messagebox.showerror("错误", "宽度和高度必须为正数")
return
# 更新当前模板
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()
self.result = {
"type": "custom",
"width": width,
"height": height,
"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
}
else:
w, h = map(int, self.ratio_var.get().split(":"))
# 更新当前模板
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()
self.result = {
"type": "preset",
"ratio": tuple([w, h]),
"width": self.width_var.get(),
"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
}
self.destroy()
except Exception as e:
messagebox.showerror("错误", f"设置模板失败: {str(e)}")
def on_cancel(self):
"""取消按钮"""
self.result = None
self.destroy()