Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Python实用开发项目案例02 -- 图片批处理工具

Python实用开发项目案例02 -- 图片批处理工具

作者头像
sergiojune
发布于 2025-04-18 09:54:40
发布于 2025-04-18 09:54:40
14400
代码可运行
举报
文章被收录于专栏:日常学python日常学python
运行总次数:0
代码可运行

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

今天,我想与大家分享一个适合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
运行
AI代码解释
复制
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
运行
AI代码解释
复制
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
运行
AI代码解释
复制
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
运行
AI代码解释
复制
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
运行
AI代码解释
复制
# -*- 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.com 删除

本文分享自 日常学python 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
完结撒花,copilot——细节决定成败!
今年五月的时候我写了一篇文章《**花了大半个月,我终于逆向分析了Github Copilot**》,受到了不少关注。当时应该还是copilot刚出不久,通过AST将webpack_modules的chunk自动拆分到不同的module文件,并通过一些语法转换让代码更易读。
孟健
2023/11/16
7020
完结撒花,copilot——细节决定成败!
copilot源码详细分析(三)ghostText核心逻辑
代码补全逻辑入口在calculateInlineCompletions 这个函数中:
孟健
2023/11/16
4890
copilot源码详细分析(三)ghostText核心逻辑
学习 koa 源码的整体架构,浅析koa洋葱模型原理和co原理
感兴趣的读者可以点击阅读。 其他源码计划中的有:express、vue-rotuer、redux、 react-redux 等源码,不知何时能写完(哭泣),欢迎持续关注我(若川)。
若川
2020/03/19
1.1K0
前端手写面试题,看这一篇就够了
题目描述:有一组版本号如下 ['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']。现在需要对其进行排序,排序的结果为 ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']
helloworld1024
2022/11/14
3320
Go Redis 中间件
本文基于 redis v6 中 WrapProcess 方法,可以对 redis 前后做操作。
王小明_HIT
2022/06/14
4500
VS Code 整活:100行代码写一个悬浮翻译插件
要说哪个插件对效率提升最大,可能各有推荐,各有千秋。但我要说对初学者,以及英文有亿点点差的同学来讲:翻译,是日常开发中必不可少的一环。在下找过N个VSCode 翻译插件 发现一个神器:
前端劝退师
2022/04/07
1.8K0
VS Code 整活:100行代码写一个悬浮翻译插件
深入koa2源码
koa是当下非常流行的node框架,相比笨重的express,koa只专注于中间件模型的建立,以及请求和响应控制权的转移。本文将以koa2为例,深入源码分析框架的实现细节。 koa2的源码位于lib目录,结构非常简单和清晰,只有四个文件,如下:
全栈程序员站长
2021/06/22
5650
copilot源码详细分析(一)从package.json说起
我们接下来重点分析一下walkthroughs、commands、keybindings、menus、configuration这五个字段。
孟健
2023/11/16
6210
copilot源码详细分析(一)从package.json说起
源码共读-Koa
Koa是基于 Node.js 平台的下一代 web 开发框架,它的源码可以看这里,本章通过源码来简绍一下Koa是怎么实现的。
kai666666
2024/07/11
1080
源码共读-Koa
[vue源码][nextTick]原理以及源码解析
nextTick Vue中的 nextTick涉及到Vue中DOM的异步更新,感觉很有意思,特意了解了一下。其中关于 nextTick的源码涉及到不少知识, nextTick 是 Vue 的一个核心实现,在介绍 Vue 的 nextTick 之前,为了方便大家理解,我先简单介绍一下 JS 的运行机制。
前端迷
2019/08/15
9210
实用的VUE系列——每天在用的Vue-SFC-Playground你真的了解吗?
声明:本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
用户7413032
2024/03/23
1.9K0
实用的VUE系列——每天在用的Vue-SFC-Playground你真的了解吗?
我逆向了GitHub Copilot,这是代码实现
众所周知,Github Copilot 是一种基于机器学习的代码自动补全工具。它使用了来自 GitHub 的大量代码作为训练数据,并使用 OpenAI 的语言模型来生成代码。Copilot 还可以学习用户的编码习惯,并根据上下文推断出正确的代码片段。在实际使用中发现大部份提示还是非常好用的,能够较为准确的推测出用户意图,甚至是基于项目其他文件的上下文进行推理。比较好奇这里是怎么做到的,于是探索了这个 VSCode 插件的详细实现。
腾讯云开发者
2023/11/03
2.8K0
我逆向了GitHub Copilot,这是代码实现
高级前端二面手写面试题(边面边更)1
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
helloworld1024
2023/01/02
5170
Vue常见面试题
虚拟 DOM (Virtual DOM )这个概念相信大家都不陌生,从 React 到 Vue ,虚拟 DOM 为这两个框架都带来了跨平台的能力(React-Native 和 Weex)
隔壁老陈
2023/03/09
2K0
Vue常见面试题
【koa快速入门】之深究原理
前两节我们已经介绍了koa的基本使用和koa项目的最佳实践,今天我们来深究下koa2的原理。
luciozhang
2023/04/22
3120
【koa快速入门】之深究原理
koa源码阅读[1]-koa与koa-compose
接上次挖的坑,对koa2.x相关的源码进行分析 第一篇。 不得不说,koa是一个很轻量、很优雅的http框架,尤其是在2.x以后移除了co的引入,使其代码变得更为清晰。
贾顺名
2019/12/09
7280
从源码分析express/koa/redux/axios等中间件的实现方式
在前端比较熟悉的框架如express、koa、redux和axios中,都提供了中间件或拦截器的功能,本文将从源码出发,分析这几个框架中对应中间件的实现原理。
周陆军博客
2023/05/14
2K0
造一轮子:vscode插件--支持json生成go struct,curl生成go代码
最近学习了一下如何写vscode插件,不得不感叹大神写的vscode框架就是厉害,简单通过配置文件加上事件处理代码就可以扩展编辑器前端的能力。膜拜之余,造了一个轮子,交互过程如下,右键json文件选择“json生成go结构体(JsonToGo)”就可以生成json文件对应的golang struct;选择“生成golang代码或者结构体->curl生成go代码(CurlToGo)”就可以从curl命令(从浏览器的debug tool直接copy过来)生成对应的golang客户端代码,简单修改即可发起http请求。
golangLeetcode
2023/03/01
1.4K0
造一轮子:vscode插件--支持json生成go struct,curl生成go代码
VSC Extension Development-Create A Code Formatter Extension
Nothing to say here, check Official Guide
szhshp
2022/09/21
4460
写一个VSCode扩展
自从使用过 VSCode 后就再也离不开 VSCode,其轻量的代码编辑器与诸多插件让多数开发者爱不释手。同样我也不例外,一年前的我甚至还特意买本《Visual Studio Code 权威指南》的书籍,来更进一步了解与使用。
愧怍
2022/12/27
2.9K0
写一个VSCode扩展
相关推荐
完结撒花,copilot——细节决定成败!
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验