127 lines
5.3 KiB
Python
127 lines
5.3 KiB
Python
|
import tkinter as tk
|
||
|
from tkinter import filedialog, messagebox
|
||
|
from PIL import Image, ImageTk
|
||
|
import os
|
||
|
|
||
|
class ImageCropApp:
|
||
|
def __init__(self, root):
|
||
|
self.root = root
|
||
|
self.root.title("Multi Image Crop Tool")
|
||
|
|
||
|
# 选择图片按钮
|
||
|
self.select_button = tk.Button(root, text="选择图片", command=self.select_images)
|
||
|
self.select_button.pack(pady=10)
|
||
|
|
||
|
# 画布,用于显示图片
|
||
|
self.canvas = tk.Canvas(root, cursor="cross")
|
||
|
self.canvas.pack(fill=tk.BOTH, expand=True)
|
||
|
|
||
|
self.images = [] # 用于保存选择的多张图片路径
|
||
|
self.image = None # 当前显示的图片
|
||
|
self.rect_start_x = 0
|
||
|
self.rect_start_y = 0
|
||
|
self.rect_end_x = 0
|
||
|
self.rect_end_y = 0
|
||
|
self.rect = None
|
||
|
self.tk_image = None
|
||
|
self.save_dir = None # 保存路径
|
||
|
self.image_display_width = 0 # 显示图片的宽度
|
||
|
self.image_display_height = 0 # 显示图片的高度
|
||
|
self.image_original_width = 0 # 图片的原始宽度
|
||
|
self.image_original_height = 0 # 图片的原始高度
|
||
|
|
||
|
# 鼠标事件
|
||
|
self.canvas.bind("<Button-1>", self.start_selection)
|
||
|
self.canvas.bind("<B1-Motion>", self.update_selection)
|
||
|
self.canvas.bind("<ButtonRelease-1>", self.finish_selection)
|
||
|
|
||
|
def select_images(self):
|
||
|
# 多文件选择
|
||
|
image_paths = filedialog.askopenfilenames(title="选择多张图片", filetypes=[("Image files", "*.jpg;*.png;*.jpeg;*.bmp"), ("All files", "*.*")])
|
||
|
if image_paths:
|
||
|
self.images = list(image_paths) # 将文件路径保存为列表
|
||
|
self.load_first_image()
|
||
|
|
||
|
def load_first_image(self):
|
||
|
# 加载第一张图片并显示在画布上
|
||
|
if self.images:
|
||
|
self.image = Image.open(self.images[0])
|
||
|
self.image_original_width, self.image_original_height = self.image.size # 获取原始尺寸
|
||
|
self.display_image()
|
||
|
|
||
|
def display_image(self):
|
||
|
# 调整图片大小适应窗口
|
||
|
self.image.thumbnail((self.root.winfo_width(), self.root.winfo_height())) # 缩放图片
|
||
|
self.tk_image = ImageTk.PhotoImage(self.image)
|
||
|
|
||
|
self.image_display_width, self.image_display_height = self.image.size # 显示的宽高
|
||
|
|
||
|
self.canvas.create_image(0, 0, anchor="nw", image=self.tk_image)
|
||
|
|
||
|
def start_selection(self, event):
|
||
|
# 记录起始点
|
||
|
self.rect_start_x = event.x
|
||
|
self.rect_start_y = event.y
|
||
|
# 创建一个选择框
|
||
|
self.rect = self.canvas.create_rectangle(self.rect_start_x, self.rect_start_y, self.rect_start_x, self.rect_start_y, outline="red", width=2)
|
||
|
|
||
|
def update_selection(self, event):
|
||
|
# 更新选择框大小
|
||
|
self.rect_end_x = event.x
|
||
|
self.rect_end_y = event.y
|
||
|
self.canvas.coords(self.rect, self.rect_start_x, self.rect_start_y, self.rect_end_x, self.rect_end_y)
|
||
|
|
||
|
def finish_selection(self, event):
|
||
|
self.rect_end_x = event.x
|
||
|
self.rect_end_y = event.y
|
||
|
self.crop_images()
|
||
|
|
||
|
def crop_images(self):
|
||
|
if self.images:
|
||
|
# 计算图像与显示窗口的比例
|
||
|
x_ratio = self.image_original_width / self.image_display_width
|
||
|
y_ratio = self.image_original_height / self.image_display_height
|
||
|
|
||
|
# 获取选择框的坐标,并转换为图片的实际坐标
|
||
|
left = min(self.rect_start_x, self.rect_end_x) * x_ratio
|
||
|
top = min(self.rect_start_y, self.rect_end_y) * y_ratio
|
||
|
right = max(self.rect_start_x, self.rect_end_x) * x_ratio
|
||
|
bottom = max(self.rect_start_y, self.rect_end_y) * y_ratio
|
||
|
|
||
|
# 让用户选择保存文件夹,只选择一次
|
||
|
if self.save_dir is None:
|
||
|
self.save_dir = filedialog.askdirectory(title="选择保存文件夹")
|
||
|
if not self.save_dir:
|
||
|
messagebox.showwarning("警告", "未选择保存文件夹,操作已取消")
|
||
|
return
|
||
|
|
||
|
for image_path in self.images:
|
||
|
img = Image.open(image_path)
|
||
|
img_original_width, img_original_height = img.size # 获取每张图片的原始大小
|
||
|
|
||
|
# 根据比例缩放选择区域并裁剪
|
||
|
left_scaled = left * (img_original_width / self.image_original_width)
|
||
|
top_scaled = top * (img_original_height / self.image_original_height)
|
||
|
right_scaled = right * (img_original_width / self.image_original_width)
|
||
|
bottom_scaled = bottom * (img_original_height / self.image_original_height)
|
||
|
|
||
|
cropped_image = img.crop((left_scaled, top_scaled, right_scaled, bottom_scaled))
|
||
|
|
||
|
# 自动生成默认文件名
|
||
|
base_name = os.path.basename(image_path)
|
||
|
default_filename = f"cropped_{base_name}"
|
||
|
|
||
|
# 保存路径与文件名
|
||
|
save_path = os.path.join(self.save_dir, default_filename)
|
||
|
cropped_image.save(save_path)
|
||
|
print(f"图片已保存到: {save_path}")
|
||
|
|
||
|
messagebox.showinfo("成功", "所有图片裁剪完成!")
|
||
|
|
||
|
# 启动 GUI 应用
|
||
|
if __name__ == "__main__":
|
||
|
root = tk.Tk()
|
||
|
app = ImageCropApp(root)
|
||
|
root.geometry("800x600") # 设置初始窗口大小
|
||
|
root.mainloop()
|