用python + PIL 实现图片格式转换工具
用python + PIL 实现图片格式转换工具
要运行该程序,需要使用第三方库PIL(pillow),详情可见https://blog.csdn.net/cnds123/article/details/126141838
格式支持:
支持常见图片格式转换(JPEG, PNG, BMP, GIF, TIFF, WEBP)
转换设置:
可调整输出图片质量(1-100)
支持尺寸调整(可保持宽高比,保持宽高比时,只需输入宽度或高度中的一个即可自动计算另一个维度)
可选是否保留元数据(EXIF信息)
输出选项:
自定义输出路径
自动生成新文件名(保留原文件名)
使用方法:
(1)通过"添加文件"或"添加文件夹"按钮选择图片
(2)设置输出格式和保存路径(若不指定输出路径默认原文件路径)
(3)根据需要调整图片质量和尺寸
(4)点击"开始转换"按钮进行转换
若输出路径有同名文件提示提供三个选项:
是:覆盖当前文件
否:跳过当前文件
取消:中止整个转换过程
注意事项:
转换透明背景图片到不支持透明的格式(如JPEG)时,背景会被自动填充为白色
质量设置对不同的格式效果不同(JPEG使用标准质量参数,PNG使用压缩级别)
保持宽高比时,只需输入宽度或高度中的一个即可自动计算另一个维度
运行界面如下:
源码如下:
import os
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from PIL import Image
from pathlib import PathSUPPORTED_FORMATS = ["JPEG", "PNG", "BMP", "GIF", "TIFF", "WEBP"]class ImageConverterApp:def __init__(self, root):self.root = rootself.root.title("图片格式转换工具")self.root.geometry("800x600")# 初始化变量self.selected_files = []self.output_format = tk.StringVar(value="JPEG")self.output_path = tk.StringVar()self.quality = tk.IntVar(value=85)self.resize_enabled = tk.BooleanVar(value=False)self.new_width = tk.IntVar(value=0)self.new_height = tk.IntVar(value=0)self.keep_ratio = tk.BooleanVar(value=True)self.keep_metadata = tk.BooleanVar(value=False)self.create_widgets()def create_widgets(self):# 文件选择区域file_frame = ttk.LabelFrame(self.root, text="选择图片")file_frame.pack(padx=10, pady=5, fill="x")ttk.Button(file_frame, text="添加文件", command=self.add_files).pack(side="left", padx=5)ttk.Button(file_frame, text="添加文件夹", command=self.add_folder).pack(side="left", padx=5)ttk.Button(file_frame, text="清空列表", command=self.clear_files).pack(side="right", padx=5)# 文件列表self.file_list = tk.Listbox(self.root, selectmode=tk.EXTENDED)self.file_list.pack(padx=10, pady=5, fill="both", expand=True)# 设置区域settings_frame = ttk.LabelFrame(self.root, text="转换设置")settings_frame.pack(padx=10, pady=5, fill="x")# 输出格式ttk.Label(settings_frame, text="输出格式:").grid(row=0, column=0, sticky="w")format_combo = ttk.Combobox(settings_frame, textvariable=self.output_format, values=SUPPORTED_FORMATS, state="readonly")format_combo.grid(row=0, column=1, padx=5, sticky="ew")# 输出路径ttk.Label(settings_frame, text="输出路径:").grid(row=1, column=0, sticky="w")ttk.Entry(settings_frame, textvariable=self.output_path).grid(row=1, column=1, columnspan=2, padx=5, sticky="ew")ttk.Button(settings_frame, text="浏览...", command=self.select_output_path).grid(row=1, column=3, padx=5)# 质量设置ttk.Label(settings_frame, text="图片质量 (1-100):").grid(row=2, column=0, sticky="w")ttk.Scale(settings_frame, from_=1, to=100, variable=self.quality, command=lambda v: self.quality.set(int(float(v)))).grid(row=2, column=1, padx=5, sticky="ew")ttk.Label(settings_frame, textvariable=self.quality).grid(row=2, column=2, padx=5)# 尺寸调整resize_frame = ttk.Frame(settings_frame)resize_frame.grid(row=3, column=0, columnspan=3, sticky="ew", pady=5)ttk.Checkbutton(resize_frame, text="调整尺寸", variable=self.resize_enabled).pack(side="left", padx=5)ttk.Entry(resize_frame, textvariable=self.new_width, width=5).pack(side="left", padx=5)ttk.Label(resize_frame, text="x").pack(side="left")ttk.Entry(resize_frame, textvariable=self.new_height, width=5).pack(side="left", padx=5)ttk.Checkbutton(resize_frame, text="保持比例", variable=self.keep_ratio).pack(side="left", padx=5)# 其他选项ttk.Checkbutton(settings_frame, text="保留元数据", variable=self.keep_metadata).grid(row=4, column=0, columnspan=3, sticky="w")# 操作按钮button_frame = ttk.Frame(self.root)button_frame.pack(padx=10, pady=10, fill="x")ttk.Button(button_frame, text="开始转换", command=self.start_conversion).pack(side="right", padx=5)ttk.Button(button_frame, text="退出", command=self.root.destroy).pack(side="right", padx=5)def add_files(self):files = filedialog.askopenfilenames(filetypes=[("图片文件", "*.jpg *.jpeg *.png *.bmp *.gif *.tiff *.webp")])if files:self.selected_files.extend(files)self.update_file_list()def add_folder(self):folder = filedialog.askdirectory()if folder:for ext in ("*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.tiff", "*.webp"):self.selected_files.extend(Path(folder).glob(ext))self.update_file_list()def clear_files(self):self.selected_files = []self.file_list.delete(0, tk.END)def update_file_list(self):self.file_list.delete(0, tk.END)for f in self.selected_files:self.file_list.insert(tk.END, str(f))def select_output_path(self):path = filedialog.askdirectory()if path:self.output_path.set(path)def start_conversion(self):if not self.selected_files:messagebox.showwarning("警告", "请先选择要转换的图片文件!")returnoutput_path = self.output_path.get() or os.path.dirname(self.selected_files[0])output_format = self.output_format.get()# 创建输出目录(如果不存在)os.makedirs(output_path, exist_ok=True)for file_path in self.selected_files:try:img = Image.open(file_path)# 处理尺寸调整if self.resize_enabled.get():width = self.new_width.get()height = self.new_height.get()original_width, original_height = img.sizeif self.keep_ratio.get():if width > 0 and height == 0:ratio = width / original_widthheight = int(original_height * ratio)elif height > 0 and width == 0:ratio = height / original_heightwidth = int(original_width * ratio)else:ratio = min(width/original_width, height/original_height)width = int(original_width * ratio)height = int(original_height * ratio)if width > 0 and height > 0:img = img.resize((width, height), Image.Resampling.LANCZOS)# 构建输出路径filename = os.path.splitext(os.path.basename(file_path))[0]output_file = os.path.join(output_path, f"{filename}.{output_format.lower()}")# 检查文件是否存在并提示if os.path.exists(output_file):response = messagebox.askyesnocancel("文件已存在",f"目标文件已存在:\n{output_file}\n\n""请选择操作:\n""• 是:覆盖当前文件\n""• 否:跳过当前文件\n""• 取消:中止全部转换",parent=self.root)if response is None: # 取消returnif not response: # 跳过continue# 构建输出路径filename = os.path.splitext(os.path.basename(file_path))[0]output_file = os.path.join(output_path, f"{filename}.{output_format.lower()}")# 保存参数save_args = {'format': output_format}if output_format == 'JPEG':save_args['quality'] = self.quality.get()save_args['optimize'] = Trueelif output_format == 'PNG':save_args['compress_level'] = 9 - int(self.quality.get() / 11.1)# 保存图片if not self.keep_metadata.get():img.info.pop('exif', None)img.save(output_file, **save_args)except Exception as e:messagebox.showerror("错误", f"处理文件 {file_path} 时出错:\n{str(e)}")continuemessagebox.showinfo("完成", "图片转换完成!")if __name__ == "__main__":root = tk.Tk()app = ImageConverterApp(root)root.mainloop()