Python实用开发项目案例02

在编程学习的道路上,实践项目是提升能力的最佳途径。

今天,我想与大家分享一个适合Python初学者的实用小项目——使用Tkinter和PIL库构建一个图片批处理工具。

这个项目不仅能帮助巩固Python基础知识,还能了解图像处理和图形界面编程的实用技能。

项目概述

  • 支持批量选择多个图片文件进行处理
  • 提供图片尺寸调整功能,可自定义目标尺寸
  • 支持多种常见图片格式转换(JPEG、PNG、BMP、GIF、TIFF)
  • 可添加自定义文字水印
  • 直观的图形界面,显示处理进度

技术要点解析

Tkinter与PIL/Pillow简介

Tkinter是Python的标准GUI库,它为Python应用程序提供了一种简单而强大的方式来创建图形界面。作为Python标准库的一部分,它无需额外安装,使用起来也相对简单。

PIL/Pillow是Python中强大的图像处理库,支持打开、操作和保存多种格式的图像文件。通过PIL,我们可以轻松实现图像调整大小、格式转换、添加水印等功能。

面向对象编程

本项目采用面向对象的方式组织代码,通过创建一个ImageProcessor类来封装所有相关功能:

  1. 界面设计:创建文件选择、参数设置和进度显示等UI元素
  2. 事件处理:处理用户的各种交互,如选择文件、设置参数和开始处理
  3. 图像处理:封装调整大小、格式转换和添加水印等功能

核心功能实现

  1. 文件选择:允许用户选择多个图片文件或输出文件夹
  2. 参数设置:获取用户设置的目标尺寸、输出格式和水印文本
  3. 图像处理:对选定的图片进行批量处理,包括调整大小、格式转换和添加水印
  4. 进度显示:实时显示处理进度,提高用户体验

代码剖析

初始化与UI创建

工具的初始化过程设置了基本窗口属性和初始变量,并调用方法创建UI组件:

代码语言:javascript代码运行次数:0运行复制
def __init__(self, root):
    self.root = root
    self.root.title("图片批处理工具")
    self.root.geometry("500x400")
    
    # 初始化参数
    self.input_folder = ""
    self.output_folder = ""
    self.watermark_text = ""
    self.target_size = (800, 600)
    self.target_format = "JPEG"
    
    # 创建UI
    self.create_widgets()

界面构建

UI构建方法创建了用户交互所需的所有元素,包括文件选择、参数设置和处理按钮:

代码语言:javascript代码运行次数:0运行复制
def create_widgets(self):
    # 修改输入为文件选择
    tk.Label(self.root, text="选择图片文件:").grid(row=0, column=0, padx=5, pady=5)
    self.input_entry = tk.Entry(self.root, width=40)
    self.input_entry.grid(row=0, column=1, padx=5, pady=5)
    tk.Button(self.root, text="浏览...", command=self.select_input_files).grid(row=0, column=2, padx=5, pady=5)
    
    # 输出文件夹
    tk.Label(self.root, text="输出文件夹:").grid(row=1, column=0, padx=5, pady=5)
    self.output_entry = tk.Entry(self.root, width=40)
    self.output_entry.grid(row=1, column=1, padx=5, pady=5)
    tk.Button(self.root, text="浏览...", command=self.select_output_folder).grid(row=1, column=2, padx=5, pady=5)
    
    # 图片大小调整
    tk.Label(self.root, text="目标尺寸 (宽x高):").grid(row=2, column=0, padx=5, pady=5)
    self.size_entry = tk.Entry(self.root)
    self.size_entry.grid(row=2, column=1, padx=5, pady=5)
    self.size_entry.insert(0, "800x600")
    
    # 图片格式转换
    tk.Label(self.root, text="输出格式:").grid(row=3, column=0, padx=5, pady=5)
    self.format_var = tk.StringVar(value="JPEG")
    formats = ["JPEG", "PNG", "BMP", "GIF", "TIFF"]
    tk.OptionMenu(self.root, self.format_var, *formats).grid(row=3, column=1, sticky="w", padx=5, pady=5)
    
    # 水印设置
    tk.Label(self.root, text="水印文字:").grid(row=4, column=0, padx=5, pady=5)
    self.watermark_entry = tk.Entry(self.root)
    self.watermark_entry.grid(row=4, column=1, padx=5, pady=5)
    
    # 处理按钮
    tk.Button(self.root, text="开始处理", command=self.process_images, height=2, width=15).grid(row=5, column=1, pady=20)
    
    # 进度显示
    self.progress_var = tk.StringVar()
    self.progress_var.set("准备就绪")
    tk.Label(self.root, textvariable=self.progress_var).grid(row=6, column=0, columnspan=3)

文件选择功能

文件选择功能允许用户选择需要处理的图片文件和输出文件夹:

代码语言:javascript代码运行次数:0运行复制
def select_input_files(self):
    filetypes = (
        ('图片文件', '*.jpg *.jpeg *.png *.bmp *.gif'),
        ('所有文件', '*.*')
    )
    self.input_files = filedialog.askopenfilenames(title="选择图片文件", filetypes=filetypes)
    self.input_entry.delete(0, tk.END)
    self.input_entry.insert(0, f"已选择 {len(self.input_files)} 个文件")

def select_output_folder(self):
    self.output_folder = filedialog.askdirectory()
    self.output_entry.delete(0, tk.END)
    self.output_entry.insert(0, self.output_folder)

图像处理核心逻辑

图像处理方法是工具的核心,它包含了批量处理图片的完整逻辑:

代码语言:javascript代码运行次数:0运行复制
def process_images(self):
    # 检查输入
    ifnot self.input_files:
        messagebox.showerror("错误", "请先选择图片文件")
        return
    
    try:
        width, height = map(int, self.size_entry.get().split("x"))
        self.target_size = (width, height)
    except:
        messagebox.showerror("错误", "请输入有效的尺寸格式 (如: 800x600)")
        return
    
    self.target_format = self.format_var.get()
    self.watermark_text = self.watermark_entry.get()
    
    ifnot self.output_folder:
        messagebox.showerror("错误", "请选择输出文件夹")
        return
    
    ifnot os.path.exists(self.output_folder):
        os.makedirs(self.output_folder)
    
    # 处理图片
    processed = 0
    for file_path in self.input_files:
        try:
            filename = os.path.basename(file_path)
            output_filename = os.path.splitext(filename)[0] + "." + self.target_format.lower()
            output_path = os.path.join(self.output_folder, output_filename)
            
            # 打开图片
            img = Image.open(file_path)
            
            # 调整大小
            img = img.resize(self.target_size, Image.LANCZOS)
            
            # 添加水印
            if self.watermark_text:
                draw = ImageDraw.Draw(img)
                font = ImageFont.load_default()
                text_width, text_height = draw.textsize(self.watermark_text, font)
                position = (img.width - text_width - 10, img.height - text_height - 10)
                draw.text(position, self.watermark_text, (255, 255, 255), font=font)
            
            # 保存图片
            img.save(output_path, self.target_format)
            
            processed += 1
            self.progress_var.set(f"已处理: {processed}/{len(self.input_files)} 张图片")
            self.root.update()
        
        except Exception as e:
            print(f"处理 {filename} 时出错: {str(e)}")
    
    messagebox.showinfo("完成", f"图片处理完成!\n共处理了 {processed} 张图片")

项目界面

完整代码

代码语言:javascript代码运行次数:0运行复制
# -*- coding: utf-8 -*-
import os
from PIL import Image, ImageDraw, ImageFont
from tkinter import filedialog, messagebox
import tkinter as tk

class ImageProcessor:
    def __init__(self, root):
        self.root = root
        self.root.title("图片批处理工具")
        self.root.geometry("500x400")
        
        # 初始化参数
        self.input_folder = ""
        self.output_folder = ""
        self.watermark_text = ""
        self.target_size = (800, 600)
        self.target_format = "JPEG"
        
        # 创建UI
        self.create_widgets()
    
    def create_widgets(self):
        # 修改输入为文件选择
        tk.Label(self.root, text="选择图片文件:").grid(row=0, column=0, padx=5, pady=5)
        self.input_entry = tk.Entry(self.root, width=40)
        self.input_entry.grid(row=0, column=1, padx=5, pady=5)
        tk.Button(self.root, text="浏览...", command=self.select_input_files).grid(row=0, column=2, padx=5, pady=5)
        
        # 输出文件夹保持不变
        tk.Label(self.root, text="输出文件夹:").grid(row=1, column=0, padx=5, pady=5)
        self.output_entry = tk.Entry(self.root, width=40)
        self.output_entry.grid(row=1, column=1, padx=5, pady=5)
        tk.Button(self.root, text="浏览...", command=self.select_output_folder).grid(row=1, column=2, padx=5, pady=5)
        
        # 图片大小调整
        tk.Label(self.root, text="目标尺寸 (宽x高):").grid(row=2, column=0, padx=5, pady=5)
        self.size_entry = tk.Entry(self.root)
        self.size_entry.grid(row=2, column=1, padx=5, pady=5)
        self.size_entry.insert(0, "800x600")
        
        # 图片格式转换
        tk.Label(self.root, text="输出格式:").grid(row=3, column=0, padx=5, pady=5)
        self.format_var = tk.StringVar(value="JPEG")
        formats = ["JPEG", "PNG", "BMP", "GIF", "TIFF"]
        tk.OptionMenu(self.root, self.format_var, *formats).grid(row=3, column=1, sticky="w", padx=5, pady=5)
        
        # 水印设置
        tk.Label(self.root, text="水印文字:").grid(row=4, column=0, padx=5, pady=5)
        self.watermark_entry = tk.Entry(self.root)
        self.watermark_entry.grid(row=4, column=1, padx=5, pady=5)
        
        # 处理按钮
        tk.Button(self.root, text="开始处理", command=self.process_images, height=2, width=15).grid(row=5, column=1, pady=20)
        
        # 进度显示
        self.progress_var = tk.StringVar()
        self.progress_var.set("准备就绪")
        tk.Label(self.root, textvariable=self.progress_var).grid(row=6, column=0, columnspan=3)
    
    def select_input_folder(self):
        self.input_folder = filedialog.askdirectory()
        self.input_entry.delete(0, tk.END)
        self.input_entry.insert(0, self.input_folder)
    
    def select_output_folder(self):
        self.output_folder = filedialog.askdirectory()
        self.output_entry.delete(0, tk.END)
        self.output_entry.insert(0, self.output_folder)
    
    def select_input_files(self):
        filetypes = (
            ('图片文件', '*.jpg *.jpeg *.png *.bmp *.gif'),
            ('所有文件', '*.*')
        )
        self.input_files = filedialog.askopenfilenames(title="选择图片文件", filetypes=filetypes)
        self.input_entry.delete(0, tk.END)
        self.input_entry.insert(0, f"已选择 {len(self.input_files)} 个文件")

    def process_images(self):
        # 修改处理逻辑
        if not self.input_files:
            messagebox.showerror("错误", "请先选择图片文件")
            return
        
        try:
            width, height = map(int, self.size_entry.get().split("x"))
            self.target_size = (width, height)
        except:
            messagebox.showerror("错误", "请输入有效的尺寸格式 (如: 800x600)")
            return
        
        self.target_format = self.format_var.get()
        
        if not os.path.exists(self.input_folder):
            messagebox.showerror("错误", "输入文件夹不存在")
            return
        
        if not os.path.exists(self.output_folder):
            os.makedirs(self.output_folder)
        
        # 处理图片
        processed = 0
        for filename in os.listdir(self.input_folder):
            if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
                try:
                    input_path = os.path.join(self.input_folder, filename)
                    output_filename = os.path.splitext(filename)[0] + "." + self.target_format.lower()
                    output_path = os.path.join(self.output_folder, output_filename)
                    
                    # 打开图片
                    img = Image.open(input_path)
                    
                    # 调整大小
                    img = img.resize(self.target_size, Image.LANCZOS)
                    
                    # 添加水印
                    if self.watermark_text:
                        draw = ImageDraw.Draw(img)
                        font = ImageFont.load_default()
                        text_width, text_height = draw.textsize(self.watermark_text, font)
                        position = (img.width - text_width - 10, img.height - text_height - 10)
                        draw.text(position, self.watermark_text, (255, 255, 255), font=font)
                    
                    # 保存图片
                    img.save(output_path, self.target_format)
                    
                    processed += 1
                    self.progress_var.set(f"已处理: {processed}/{len(self.input_files)} 张图片")
                    self.root.update()
                
                except Exception as e:
                    print(f"处理 {filename} 时出错: {str(e)}")
        
        messagebox.showinfo("完成", f"图片处理完成!\n共处理了 {processed} 张图片")

if __name__ == "__main__":
    root = tk.Tk()
    app = ImageProcessor(root)
    root.mainloop()

总结

这个图片批处理工具项目虽小,但涵盖了Python图像处理和GUI开发的重要概念。通过实现这个工具,在实践中学习了Tkinter创建用户界面、PIL/Pillow处理图像、面向对象编程和文件系统操作等关键技能。它将编程理论与实际应用紧密结合,不仅帮助提升Python技能,更能体验到编程解决实际问题的成就感。

在下一篇文章中,我们将继续探索更多Python实用小项目,不断丰富我们的编程技能库。敬请期待!

如果你觉得文章还不错,请大家 点赞、分享、留言 下,因为这将是我持续输出更多优质文章的最强动力!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-04-17,如有侵权请联系 cloudcommunity@tencent 删除python图像处理self工具开发