帮我增加一个功能,框选好的尺寸可以进行保存在主界面,方便下次点击就能调用 import os import tkinter as tk from tkinter import filedialog, messagebox, ttk from PIL import Image, ImageTk, UnidentifiedImageError class ImageCropperApp: def __init__(self, root): self.root = root self.root.title("批量图片裁剪工具") self.root.geometry("900x600") self.root.minsize(800, 500) # 设置中文字体支持 self.style = ttk.Style() self.style.configure('.', font=('SimHei', 10)) # 初始化变量 self.images = [] # 存储所有待处理图片路径 self.current_image_index = 0 self.current_image = None self.current_photo = None self.canvas_image = None self.selection_start = None self.selection_rect = None self.crop_box = None self.output_dir = "" # 创建界面 self.create_widgets() def create_widgets(self): # 顶部控制区 top_frame = ttk.Frame(self.root, padding=10) top_frame.pack(fill=tk.X) ttk.Button(top_frame, text="选择图片文件夹", command=self.select_image_folder).pack(side=tk.LEFT, padx=5) ttk.Button(top_frame, text="设置输出文件夹", command=self.set_output_folder).pack(side=tk.LEFT, padx=5) self.status_var = tk.StringVar(value="请选择图片文件夹") ttk.Label(top_frame, textvariable=self.status_var).pack(side=tk.LEFT, padx=10) # 中部画布区 self.canvas_frame = ttk.Frame(self.root) self.canvas_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) self.canvas = tk.Canvas(self.canvas_frame, bg="white", highlightthickness=1) self.canvas.pack(fill=tk.BOTH, expand=True) # 底部控制区 bottom_frame = ttk.Frame(self.root, padding=10) bottom_frame.pack(fill=tk.X) ttk.Button(bottom_frame, text="上一张", command=self.prev_image).pack(side=tk.LEFT, padx=5) ttk.Button(bottom_frame, text="下一张", command=self.next_image).pack(side=tk.LEFT, padx=5) ttk.Label(bottom_frame, text="裁剪尺寸:").pack(side=tk.LEFT, padx=5) self.width_var = tk.StringVar(value="200") self.height_var = tk.StringVar(value="200") ttk.Entry(bottom_frame, textvariable=self.width_var, width=5).pack(side=tk.LEFT, padx=2) ttk.Label(bottom_frame, text="x").pack(side=tk.LEFT, padx=2) ttk.Entry(bottom_frame, textvariable=self.height_var, width=5).pack(side=tk.LEFT, padx=2) ttk.Button(bottom_frame, text="应用裁剪尺寸", command=self.apply_custom_crop_size).pack(side=tk.LEFT, padx=5) ttk.Button(bottom_frame, text="批量裁剪全部图片", command=self.batch_crop_images).pack(side=tk.LEFT, padx=5) self.image_counter = tk.StringVar(value="0/0") ttk.Label(bottom_frame, textvariable=self.image_counter).pack(side=tk.RIGHT, padx=10) # 绑定画布事件 self.canvas.bind("<Button-1>", self.on_canvas_click) self.canvas.bind("<B1-Motion>", self.on_canvas_drag) self.canvas.bind("<ButtonRelease-1>", self.on_canvas_release) # 滚动支持 self.canvas_frame.bind("<Configure>", self.on_canvas_configure) self.canvas.bind("<MouseWheel>", self.on_mousewheel) def select_image_folder(self): folder = filedialog.askdirectory(title="选择图片文件夹") if folder: self.images = [] for filename in os.listdir(folder): if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')): self.images.append(os.path.join(folder, filename)) if self.images: self.current_image_index = 0 self.load_image() self.status_var.set(f"已加载 {len(self.images)} 张图片") self.image_counter.set(f"{self.current_image_index + 1}/{len(self.images)}") else: messagebox.showinfo("提示", "所选文件夹中没有找到支持的图片格式") def set_output_folder(self): folder = filedialog.askdirectory(title="设置输出文件夹") if folder: self.output_dir = folder self.status_var.set(f"输出文件夹已设置: {folder}") def load_image(self): if 0 <= self.current_image_index < len(self.images): try: self.current_image = Image.open(self.images[self.current_image_index]) self.display_image() self.crop_box = None self.selection_start = None self.selection_rect = None except UnidentifiedImageError: messagebox.showerror("错误", f"无法打开图片: {self.images[self.current_image_index]}") self.next_image() def display_image(self): # 清除画布 self.canvas.delete("all") # 调整图片大小以适应画布 canvas_width = self.canvas.winfo_width() canvas_height = self.canvas.winfo_height() if canvas_width <= 1 or canvas_height <= 1: # 画布尚未完全初始化 return img_width, img_height = self.current_image.size ratio = min(canvas_width / img_width, canvas_height / img_height) display_width = int(img_width * ratio) display_height = int(img_height * ratio) self.current_photo = ImageTk.PhotoImage( self.current_image.resize((display_width, display_height), Image.LANCZOS)) self.canvas_image = self.canvas.create_image(canvas_width // 2, canvas_height // 2, image=self.current_photo) # 如果有裁剪框,重新绘制 if self.crop_box: x1, y1, x2, y2 = self.crop_box # 转换为画布坐标 x1_canvas = x1 * ratio + canvas_width // 2 - display_width // 2 y1_canvas = y1 * ratio + canvas_height // 2 - display_height // 2 x2_canvas = x2 * ratio + canvas_width // 2 - display_width // 2 y2_canvas = y2 * ratio + canvas_height // 2 - display_height // 2 self.selection_rect = self.canvas.create_rectangle( x1_canvas, y1_canvas, x2_canvas, y2_canvas, outline="red", width=2 ) def on_canvas_click(self, event): if not self.current_image: return self.selection_start = (event.x, event.y) # 如果已经有选择框,删除它 if self.selection_rect: self.canvas.delete(self.selection_rect) self.selection_rect = None def on_canvas_drag(self, event): if not self.current_image or not self.selection_start: return # 如果已经有选择框,删除它 if self.selection_rect: self.canvas.delete(self.selection_rect) # 创建新的选择框 x1, y1 = self.selection_start x2, y2 = event.x, event.y self.selection_rect = self.canvas.create_rectangle(x1, y1, x2, y2, outline="red", width=2) def on_canvas_release(self, event): if not self.current_image or not self.selection_start: return # 获取选择框的坐标 x1, y1 = self.selection_start x2, y2 = event.x, event.y # 确保坐标按正确顺序排列 x1, x2 = min(x1, x2), max(x1, x2) y1, y2 = min(y1, y2), max(y1, y2) # 转换为图像坐标 canvas_width = self.canvas.winfo_width() canvas_height = self.canvas.winfo_height() img_width, img_height = self.current_image.size ratio = min(canvas_width / img_width, canvas_height / img_height) img_x1 = int((x1 - canvas_width // 2 + img_width * ratio // 2) / ratio) img_y1 = int((y1 - canvas_height // 2 + img_height * ratio // 2) / ratio) img_x2 = int((x2 - canvas_width // 2 + img_width * ratio // 2) / ratio) img_y2 = int((y2 - canvas_height // 2 + img_height * ratio // 2) / ratio) # 确保坐标在图像范围内 img_x1 = max(0, min(img_x1, img_width)) img_y1 = max(0, min(img_y1, img_height)) img_x2 = max(0, min(img_x2, img_width)) img_y2 = max(0, min(img_y2, img_height)) # 保存裁剪框 self.crop_box = (img_x1, img_y1, img_x2, img_y2) # 显示裁剪尺寸信息 width = img_x2 - img_x1 height = img_y2 - img_y1 self.status_var.set(f"已选择裁剪区域: {width}x{height} 像素") def apply_custom_crop_size(self): try: width = int(self.width_var.get()) height = int(self.height_var.get()) if width <= 0 or height <= 0: messagebox.showerror("错误", "裁剪尺寸必须为正整数") return if not self.crop_box: messagebox.showinfo("提示", "请先在图片上选择一个裁剪区域") return # 获取当前裁剪框的中心点 x1, y1, x2, y2 = self.crop_box center_x = (x1 + x2) // 2 center_y = (y1 + y2) // 2 # 计算新的裁剪框坐标 new_x1 = max(0, center_x - width // 2) new_y1 = max(0, center_y - height // 2) new_x2 = min(self.current_image.width, new_x1 + width) new_y2 = min(self.current_image.height, new_y1 + height) # 调整裁剪框大小,保持中心点不变 if new_x2 - new_x1 < width: diff = width - (new_x2 - new_x1) new_x1 = max(0, new_x1 - diff // 2) new_x2 = new_x1 + width if new_y2 - new_y1 < height: diff = height - (new_y2 - new_y1) new_y1 = max(0, new_y1 - diff // 2) new_y2 = new_y1 + height self.crop_box = (new_x1, new_y1, new_x2, new_y2) # 刷新显示 self.display_image() self.status_var.set(f"已应用自定义裁剪尺寸: {width}x{height} 像素") except ValueError: messagebox.showerror("错误", "请输入有效的整数尺寸") def crop_current_image(self): if not self.crop_box: messagebox.showinfo("提示", "请先选择裁剪区域") return if not self.output_dir: messagebox.showinfo("提示", "请先设置输出文件夹") return x1, y1, x2, y2 = self.crop_box cropped_img = self.current_image.crop((x1, y1, x2, y2)) # 保存裁剪后的图片 filename = os.path.basename(self.images[self.current_image_index]) name, ext = os.path.splitext(filename) output_path = os.path.join(self.output_dir, f"{name}_cropped{ext}") try: # 保存时保持原画质 if ext.lower() in ('.jpg', '.jpeg'): cropped_img.save(output_path, quality=100, subsampling=0) else: cropped_img.save(output_path) self.status_var.set(f"已裁剪并保存图片: {output_path}") return True except Exception as e: messagebox.showerror("错误", f"保存图片时出错: {str(e)}") return False def batch_crop_images(self): if not self.crop_box: messagebox.showinfo("提示", "请先在第一张图片上选择裁剪区域") return if not self.output_dir: messagebox.showinfo("提示", "请先设置输出文件夹") return total = len(self.images) success_count = 0 error_count = 0 # 保存当前状态 current_index = self.current_image_index original_crop_box = self.crop_box # 批量处理所有图片 for i, img_path in enumerate(self.images): try: # 更新状态 self.status_var.set(f"正在处理图片 {i + 1}/{total}...") self.root.update() # 打开图片 self.current_image = Image.open(img_path) # 使用相同的裁剪框 self.crop_box = original_crop_box # 裁剪并保存 x1, y1, x2, y2 = self.crop_box cropped_img = self.current_image.crop((x1, y1, x2, y2)) filename = os.path.basename(img_path) name, ext = os.path.splitext(filename) output_path = os.path.join(self.output_dir, f"{name}_cropped{ext}") if ext.lower() in ('.jpg', '.jpeg'): cropped_img.save(output_path, quality=100, subsampling=0) else: cropped_img.save(output_path) success_count += 1 except Exception as e: error_count += 1 print(f"处理图片 {img_path} 时出错: {str(e)}") # 恢复状态 self.current_image_index = current_index self.load_image() # 显示结果 result_msg = f"批量裁剪完成!\n成功: {success_count} 张\n失败: {error_count} 张" self.status_var.set(result_msg) messagebox.showinfo("批量裁剪结果", result_msg) def prev_image(self): if self.images and self.current_image_index > 0: self.current_image_index -= 1 self.load_image() self.image_counter.set(f"{self.current_image_index + 1}/{len(self.images)}") def next_image(self): if self.images and self.current_image_index < len(self.images) - 1: self.current_image_index += 1 self.load_image() self.image_counter.set(f"{self.current_image_index + 1}/{len(self.images)}") def on_canvas_configure(self, event): # 画布大小改变时重绘图像 if self.current_image: self.display_image() def on_mousewheel(self, event): # 处理鼠标滚轮缩放 if self.current_image: # 缩放逻辑可以在这里实现 pass if __name__ == "__main__": root = tk.Tk() app = ImageCropperApp(root) root.mainloop()
时间: 2025-07-20 13:19:51 浏览: 19
<think>我们需要添加一个功能:允许用户保存当前设置的裁剪框尺寸(以及位置?),并在主界面提供快速调用这些保存的尺寸的按钮。
但是,根据需求描述,只要求保存尺寸(宽高),不要求保存位置。因为位置是针对每张图片可能不同的,而尺寸是通用的。
所以,我们计划:
1. 在主界面上增加一个“保存当前尺寸”的按钮,点击后会将当前裁剪框的宽高保存到一个列表中(同时可以给这个尺寸命名?)。
由于需求是方便下次点击调用,我们可以让用户输入一个名称,也可以自动生成名称(如“尺寸1”、“尺寸2”)。但为了用户友好,我们让用户输入一个名称。
2. 在主界面上显示已保存的尺寸列表(比如用下拉框或按钮列表),用户点击某个已保存的尺寸,就会将当前裁剪框的尺寸设置为该尺寸(同时保持当前裁剪框的中心位置不变?)。
注意:这里我们只设置尺寸,不改变位置。因为位置是用户每张图片自己选择的。所以,我们只需要将当前裁剪框的尺寸改为保存的尺寸,而中心点不变(这样用户可以在同一位置用不同尺寸裁剪)。
但是,原功能中已经有一个“应用裁剪尺寸”按钮,它根据输入框的宽高来调整当前裁剪框的尺寸(中心不变)。所以,我们保存尺寸后,当用户点击某个保存的尺寸,我们只需要将输入框的宽高设置为该尺寸,然后调用`apply_custom_crop_size`方法即可。
因此,我们可以这样设计:
- 添加一个“保存当前尺寸”按钮,点击后弹出对话框让用户输入尺寸名称(同时显示当前裁剪框的宽高)。
- 将保存的尺寸以列表形式存储(在内存中,同时可以持久化到文件,这样下次启动也能用?需求没有明确,但为了下次启动也能用,我们保存到文件)。
- 在界面上显示保存的尺寸列表(比如用一组按钮,或者一个下拉菜单,或者一个列表框)。这里为了快速调用,使用一组按钮(水平排列或垂直排列)可能更直观,但考虑到可能保存多个,我们使用一个下拉框(Combobox)和一个“应用”按钮,或者直接点击下拉框的选项就应用?或者使用一组按钮放在一个可滚动的区域。
考虑到界面空间,我们在顶部控制区增加一个区域来显示保存的尺寸按钮。但是顶部空间有限,我们可以放在底部控制区上面,或者左侧边栏。
由于我们的界面目前是上下结构,我们可以将底部控制区上面增加一个区域(比如在画布下面,底部控制区上面)作为“保存尺寸”的按钮区。
具体修改步骤:
1. 在类中增加一个列表属性来存储保存的尺寸,每个元素是一个字典:{"name": "名称", "width": 宽, "height": 高}
2. 增加一个保存尺寸的方法:`save_current_size`,弹出对话框让用户输入名称,然后保存当前裁剪框的尺寸(如果没有裁剪框,则提示先选择)。
3. 增加一个应用保存尺寸的方法:`apply_saved_size`,当用户选择一个保存的尺寸时,将输入框的宽高设置为该尺寸,并调用`apply_custom_crop_size`方法(这样就会调整当前裁剪框的尺寸)。
4. 在界面上增加一个区域(Frame)用于显示保存的尺寸按钮(或者一个下拉框和按钮)。为了节省空间,我们可以用下拉框(Combobox)和一个“应用”按钮,再加上一个“保存”按钮。
但是,为了更直观的快速调用,我们可以用一组按钮,每个按钮显示一个保存的尺寸名称(如“名称(宽x高)”),点击按钮就应用该尺寸。
由于保存的尺寸可能很多,我们可以用一个可滚动的区域来放置这些按钮。
考虑到界面复杂度,我们这里采用一个下拉框(显示保存的尺寸名称)和两个按钮(“应用”和“保存”)。另外,我们还需要一个“删除”按钮来删除已保存的尺寸?但是需求没有要求,可以先不做。
因此,我们在顶部控制区增加如下控件:
- 一个标签:“预设尺寸”
- 一个Combobox,用于选择保存的尺寸
- 一个“保存当前尺寸”按钮(已经有一个“保存”按钮了,但为了避免混淆,我们叫“保存尺寸”)
- 一个“应用尺寸”按钮
另外,我们需要将保存的尺寸持久化到文件,这样下次启动还能用。我们可以用一个json文件保存在程序所在目录。
具体代码修改:
1. 在`__init__`方法中:
self.saved_sizes = [] # 存储保存的尺寸列表
self.load_saved_sizes() # 从文件加载保存的尺寸
2. 在`create_widgets`方法中,在顶部控制区(top_frame)增加控件:
# 在顶部控制区增加预设尺寸相关控件
ttk.Label(top_frame, text="预设尺寸:").pack(side=tk.LEFT, padx=5)
self.size_combo = ttk.Combobox(top_frame, state="readonly", width=15)
self.size_combo.pack(side=tk.LEFT, padx=5)
ttk.Button(top_frame, text="应用尺寸", command=self.apply_saved_size).pack(side=tk.LEFT, padx=2)
ttk.Button(top_frame, text="保存当前尺寸", command=self.save_current_size).pack(side=tk.LEFT, padx=2)
然后,在初始化部分更新下拉框的列表:
self.update_size_combo()
3. 实现`load_saved_sizes`和`save_sizes_to_file`方法,用于从文件加载和保存尺寸列表。文件名为"crop_sizes.json"。
4. 实现`update_size_combo`方法,将self.saved_sizes中的每个尺寸的名称组成列表,设置到下拉框中。
5. 实现`save_current_size`方法:
- 检查是否有裁剪框,没有则提示。
- 计算当前裁剪框的宽高(x2-x1, y2-y1)。
- 弹出输入对话框(使用simpledialog.askstring)让用户输入名称。
- 如果输入的名称不为空,且不与已有名称重复(如果重复,可以提示覆盖?或者我们允许重复,但应用时可能会有多个相同名称?为了避免混淆,我们不允许重复,如果重复则提示用户重新输入或覆盖)。
- 将新的尺寸添加到saved_sizes列表中(如果重复,先删除旧的,再添加新的;或者不允许重复,则让用户重新输入)。
- 更新下拉框,并保存到文件。
6. 实现`apply_saved_size`方法:
- 从下拉框中获取当前选中的尺寸名称。
- 在saved_sizes中查找该名称对应的尺寸。
- 将宽度和高度设置到输入框(width_var和height_var)中。
- 调用`apply_custom_crop_size`方法(注意:这个方法会调整当前裁剪框的尺寸,保持中心不变)。
7. 注意:在应用尺寸时,如果当前没有裁剪框,那么先要创建一个默认的裁剪框吗?原逻辑中,如果没有裁剪框,点击“应用裁剪尺寸”会提示先选择区域。所以我们这里也遵循同样的逻辑,即如果没有裁剪框,则提示先选择区域。
但是,我们可以在应用保存尺寸的时候,如果没有裁剪框,我们可以自动创建一个以图片中心为基准的裁剪框?这样更友好。但原功能没有,为了保持一致,我们暂时不自动创建,而是提示用户。
考虑到代码复用,我们可以修改`apply_custom_crop_size`方法,使其可以在没有裁剪框时,使用图片中心来创建裁剪框。但原需求没有要求,我们这里先不改变原逻辑,所以当没有裁剪框时,点击“应用尺寸”会弹出提示。
因此,在`apply_saved_size`方法中,我们只是设置了输入框的值,并调用了`apply_custom_crop_size`,所以它会检查是否有裁剪框。
8. 另外,在批量裁剪时,我们使用的是同一个裁剪框(位置和尺寸),所以保存的尺寸只用于调整当前裁剪框,不会影响批量裁剪(因为批量裁剪使用的是当前裁剪框,而我们在调整尺寸后,当前裁剪框已经被更新了)。
下面我们开始修改代码。
注意:持久化文件操作,我们使用json。文件保存在当前工作目录下的"crop_sizes.json"。
修改后的代码:</think>我将为您添加保存和调用裁剪尺寸的功能。以下是修改后的代码,新增功能包括:
1. 保存当前裁剪框尺寸到预设列表
2. 在主界面显示预设尺寸按钮
3. 点击预设按钮自动应用对应尺寸
```python
import os
import json
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from PIL import Image, ImageTk, UnidentifiedImageError
class ImageCropperApp:
def __init__(self, root):
self.root = root
self.root.title("批量图片裁剪工具")
self.root.geometry("900x600")
self.root.minsize(800, 500)
# 设置中文字体支持
self.style = ttk.Style()
self.style.configure('.', font=('SimHei', 10))
# 初始化变量
self.images = [] # 存储所有待处理图片路径
self.current_image_index = 0
self.current_image = None
self.current_photo = None
self.canvas_image = None
self.selection_start = None
self.selection_rect = None
self.crop_box = None
self.output_dir = ""
self.saved_sizes = [] # 存储保存的尺寸预设
self.PRESETS_FILE = "crop_presets.json" # 预设尺寸保存文件
# 创建界面
self.create_widgets()
# 加载保存的尺寸预设
self.load_saved_sizes()
def create_widgets(self):
# 顶部控制区
top_frame = ttk.Frame(self.root, padding=10)
top_frame.pack(fill=tk.X)
ttk.Button(top_frame, text="选择图片文件夹", command=self.select_image_folder).pack(side=tk.LEFT, padx=5)
ttk.Button(top_frame, text="设置输出文件夹", command=self.set_output_folder).pack(side=tk.LEFT, padx=5)
self.status_var = tk.StringVar(value="请选择图片文件夹")
ttk.Label(top_frame, textvariable=self.status_var).pack(side=tk.LEFT, padx=10)
# 预设尺寸区域
presets_frame = ttk.LabelFrame(self.root, text="预设尺寸", padding=5)
presets_frame.pack(fill=tk.X, padx=10, pady=5)
# 使用Canvas和Frame实现可滚动的预设区域
self.presets_canvas = tk.Canvas(presets_frame, height=40, highlightthickness=0)
self.presets_scrollbar = ttk.Scrollbar(presets_frame, orient="horizontal", command=self.presets_canvas.xview)
self.presets_canvas.configure(xscrollcommand=self.presets_scrollbar.set)
self.presets_container = ttk.Frame(self.presets_canvas)
self.presets_container.bind("<Configure>", lambda e: self.presets_canvas.configure(scrollregion=self.presets_canvas.bbox("all")))
self.presets_canvas.create_window((0, 0), window=self.presets_container, anchor="nw")
self.presets_scrollbar.pack(side="bottom", fill="x")
self.presets_canvas.pack(side="top", fill="x", expand=True)
# 中部画布区
self.canvas_frame = ttk.Frame(self.root)
self.canvas_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
self.canvas = tk.Canvas(self.canvas_frame, bg="white", highlightthickness=1)
self.canvas.pack(fill=tk.BOTH, expand=True)
# 底部控制区
bottom_frame = ttk.Frame(self.root, padding=10)
bottom_frame.pack(fill=tk.X)
ttk.Button(bottom_frame, text="上一张", command=self.prev_image).pack(side=tk.LEFT, padx=5)
ttk.Button(bottom_frame, text="下一张", command=self.next_image).pack(side=tk.LEFT, padx=5)
ttk.Label(bottom_frame, text="裁剪尺寸:").pack(side=tk.LEFT, padx=5)
self.width_var = tk.StringVar(value="200")
self.height_var = tk.StringVar(value="200")
ttk.Entry(bottom_frame, textvariable=self.width_var, width=5).pack(side=tk.LEFT, padx=2)
ttk.Label(bottom_frame, text="x").pack(side=tk.LEFT, padx=2)
ttk.Entry(bottom_frame, textvariable=self.height_var, width=5).pack(side=tk.LEFT, padx=2)
ttk.Button(bottom_frame, text="应用裁剪尺寸", command=self.apply_custom_crop_size).pack(side=tk.LEFT, padx=5)
ttk.Button(bottom_frame, text="保存当前尺寸", command=self.save_current_size).pack(side=tk.LEFT, padx=5) # 新增按钮
ttk.Button(bottom_frame, text="批量裁剪全部图片", command=self.batch_crop_images).pack(side=tk.LEFT, padx=5)
self.image_counter = tk.StringVar(value="0/0")
ttk.Label(bottom_frame, textvariable=self.image_counter).pack(side=tk.RIGHT, padx=10)
# 绑定画布事件
self.canvas.bind("<Button-1>", self.on_canvas_click)
self.canvas.bind("<B1-Motion>", self.on_canvas_drag)
self.canvas.bind("<ButtonRelease-1>", self.on_canvas_release)
# 滚动支持
self.canvas_frame.bind("<Configure>", self.on_canvas_configure)
self.canvas.bind("<MouseWheel>", self.on_mousewheel)
# 新增功能:加载保存的尺寸预设
def load_saved_sizes(self):
if os.path.exists(self.PRESETS_FILE):
try:
with open(self.PRESETS_FILE, 'r') as f:
self.saved_sizes = json.load(f)
self.update_presets_buttons()
except:
self.saved_sizes = []
else:
self.saved_sizes = []
# 新增功能:保存尺寸预设到文件
def save_sizes_to_file(self):
with open(self.PRESETS_FILE, 'w') as f:
json.dump(self.saved_sizes, f)
# 新增功能:更新预设尺寸按钮
def update_presets_buttons(self):
# 清除现有按钮
for widget in self.presets_container.winfo_children():
widget.destroy()
# 创建新按钮
for i, size in enumerate(self.saved_sizes):
w, h = size
btn = ttk.Button(
self.presets_container,
text=f"{w}x{h}",
width=8,
command=lambda w=w, h=h: self.apply_saved_size(w, h)
)
btn.pack(side=tk.LEFT, padx=5, pady=2)
# 添加一个清除按钮
clear_btn = ttk.Button(
self.presets_container,
text="清除预设",
width=8,
command=self.clear_saved_sizes
)
clear_btn.pack(side=tk.LEFT, padx=5, pady=2)
# 新增功能:应用保存的尺寸
def apply_saved_size(self, width, height):
if not self.crop_box:
messagebox.showinfo("提示", "请先在图片上选择一个裁剪区域")
return
# 更新输入框
self.width_var.set(str(width))
self.height_var.set(str(height))
# 应用尺寸
self.apply_custom_crop_size()
self.status_var.set(f"已应用预设尺寸: {width}x{height} 像素")
# 新增功能:清除所有保存的尺寸
def clear_saved_sizes(self):
if self.saved_sizes:
if messagebox.askyesno("确认", "确定要清除所有保存的尺寸预设吗?"):
self.saved_sizes = []
self.save_sizes_to_file()
self.update_presets_buttons()
# 新增功能:保存当前尺寸
def save_current_size(self):
if not self.crop_box:
messagebox.showinfo("提示", "请先在图片上选择一个裁剪区域")
return
# 获取当前尺寸
x1, y1, x2, y2 = self.crop_box
width = x2 - x1
height = y2 - y1
# 创建尺寸元组
size = (width, height)
# 检查是否已存在相同尺寸
if size in self.saved_sizes:
messagebox.showinfo("提示", "该尺寸已保存")
return
# 添加到保存列表
self.saved_sizes.append(size)
self.save_sizes_to_file()
self.update_presets_buttons()
self.status_var.set(f"已保存尺寸: {width}x{height} 像素")
# 以下为原有方法保持不变,仅添加了注释
def select_image_folder(self):
# ...(原有代码不变)...
def set_output_folder(self):
# ...(原有代码不变)...
def load_image(self):
# ...(原有代码不变)...
def display_image(self):
# ...(原有代码不变)...
def on_canvas_click(self, event):
# ...(原有代码不变)...
def on_canvas_drag(self, event):
# ...(原有代码不变)...
def on_canvas_release(self, event):
# ...(原有代码不变)...
def apply_custom_crop_size(self):
# ...(原有代码不变)...
def crop_current_image(self):
# ...(原有代码不变)...
def batch_crop_images(self):
# ...(原有代码不变)...
def prev_image(self):
# ...(原有代码不变)...
def next_image(self):
# ...(原有代码不变)...
def on_canvas_configure(self, event):
# ...(原有代码不变)...
def on_mousewheel(self, event):
# ...(原有代码不变)...
if __name__ == "__main__":
root = tk.Tk()
app = ImageCropperApp(root)
root.mainloop()
```
### 新增功能详细说明:
1. **尺寸预设存储**:
- 使用JSON文件(`crop_presets.json`)保存所有预设尺寸
- `load_saved_sizes()`方法在程序启动时加载预设
- `save_sizes_to_file()`方法在保存新尺寸时更新文件
2. **预设尺寸界面**:
- 在主界面顶部添加预设尺寸区域(可水平滚动)
- 每个预设尺寸显示为按钮,格式为"宽x高"
- 添加"清除预设"按钮移除所有保存的尺寸
3. **功能实现**:
- **保存当前尺寸**:点击"保存当前尺寸"按钮将当前裁剪框尺寸添加到预设
- **应用预设尺寸**:点击预设按钮自动设置尺寸并应用到当前裁剪框
- **清除预设**:一键移除所有保存的尺寸
4. **用户体验优化**:
- 检查重复尺寸避免重复保存
- 添加确认对话框防止误操作
- 状态栏显示操作反馈
### 使用流程:
1. 选择图片并绘制裁剪框
2. 点击"保存当前尺寸"将尺寸添加到预设区
3. 后续使用时,直接点击预设按钮即可应用对应尺寸
4. 点击"清除预设"可移除所有保存的尺寸
### 注意事项:
1. 预设尺寸存储在程序同目录的`crop_presets.json`文件中
2. 预设尺寸仅保存宽高信息,不包含位置信息
3. 应用预设尺寸时需要先有一个裁剪框作为基准位置
阅读全文
相关推荐




















