当前位置: 首页 > news >正文

借助ChatGPT快速开发图片转PDF的Python工具

一、开发背景与适用场景

随着数字文档处理需求的激增,图片转PDF的需求日益广泛。从学生提交图像化作业,到教师整合扫描试卷等资料,再到行政人员归档证件照片,工作中的方方面面都离不开图片的处理。如何高效、批量地将多个图片文件整理为一个标准PDF文件,成为日常办公中的痛点之一。

尽管市面上已有诸多工具支持图像转PDF,但大多数要么受限于平台(如仅支持Windows或Web)、要么操作繁琐、功能单一。而借助Python强大的图像处理与GUI库,我们可以快速构建一款轻量级、跨平台、功能丰富的图片转PDF工具,适用于:

  • 教育场景下学生作业或试卷扫描;
  • 办公场景下的证件照片批量归档;
  • 图片编辑后统一格式输出;
  • 快速制作图文资料手册等。

软件的主界面

二、主要功能与技术特点

本项目以 Tkinter 为GUI主框架,集成 PIL (Pillow) 处理图像、threading 异步转PDF、shutil 文件管理等模块,具备如下功能:

1. 多图管理与预览

  • 支持批量添加、删除、清空图片;
  • 支持上下移动图片列表中的文件排序,决定PDF页序;
  • 实时图片预览,并在右侧展示缩略图;

2. 图片编辑操作

  • 支持图片的 90° 左右旋转;
  • 支持水平 / 垂直翻转;
  • 支持单张图片的“重置”功能,回到原始状态;
  • 所有图像操作均在不影响原图基础上完成,保留数据完整性;

3. 文件命名与批处理

  • 用户可输入模板(如“Page_##.jpg”)对所有图片批量重命名; 比如你们把Page改为作业_##.jpg,生成的图片就变成作业_01.jpg, 作业_02.jpg,作业_03.jpg
  • 自动创建名为 renamed_images/ 的目录并保存副本,确保原图不被覆盖;

图片重命名

4. PDF转换与保存

  • 一键将所有图片按设定顺序合成并转化为PDF文件;
  • 异步转换不阻塞界面响应,转化速度高;
  • 支持软件底部进度条反馈转换进度;
  • 自动处理图像色彩模式为PDF兼容的RGB格式;

5. 人性化交互设计

  • 键盘快捷操作支持:向上 / 向下键调整顺序,Delete键删除选中;
  • 弹窗提示操作状态,如保存路径、错误提示、任务完成反馈等。

三、使用方法与操作流程

1. 启动程序

运行脚本后,程序将自动启动一个800×600的窗口,主界面分为顶部按钮栏、左侧列表框和右侧预览区。

2. 添加与管理图片

点击“添加图片”可选取多张图片文件导入,随后可在列表中拖动排序,或使用“上移 / 下移”按钮调整顺序。

3. 图像编辑与重命名

选中某张图片后,可以在右侧操作区执行旋转、翻转等编辑操作。如需统一重命名图片,点击“批量重命名”,输入模板后自动保存副本到 renamed_images/ 文件夹中。

4. 转换与保存PDF

点击“转换为PDF”,选择保存位置后,程序将自动执行图像转换、拼接与导出过程。用户可实时查看进度条反馈。

四、代码展示

本项目采用类的写法,主要运用了Python的自带的标准库如os,shutil等,完整的代码如下:

import os
import shutil
import threading
import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog, ttk
from PIL import Image, ImageTkclass ImageToPDFApp:def __init__(self, root):self.root = rootself.root.title("图片转PDF工具")self.root.geometry("800x600")self.image_paths = []self.modified_images = {}self.last_dir = os.path.expanduser("~")self.renamed_dir = os.path.join(os.getcwd(), "renamed_images")self.build_ui()def build_ui(self):top_frame = tk.Frame(self.root, bg="#f0f0f0")top_frame.pack(fill=tk.X, padx=5, pady=5)buttons = [("添加图片", self.add_images),("删除选中", self.remove_selected),("上移", self.move_up),("下移", self.move_down),("清空列表", self.clear_list),("转换为PDF", self.convert_to_pdf)]for text, cmd in buttons:b = tk.Button(top_frame, text=text, command=cmd, width=12, font=("Times New Roman", 12))b.pack(side=tk.LEFT, padx=3, pady=2)middle_frame = tk.Frame(self.root)middle_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)left_frame = tk.Frame(middle_frame)left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)self.lst_images = tk.Listbox(left_frame)self.lst_images.pack(fill=tk.BOTH, expand=True)self.lst_images.bind("<<ListboxSelect>>", self.update_preview)#增加快捷键self.lst_images.bind("<Up>", self.on_key_up)self.lst_images.bind("<Down>", self.on_key_down)self.lst_images.bind("<Delete>", self.on_key_delete)preview_frame = tk.Frame(middle_frame)preview_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)self.preview_label = tk.Label(preview_frame, text="图片预览", relief=tk.SUNKEN, bg="white")self.preview_label.pack(side=tk.LEFT, expand=True, fill=tk.BOTH, padx=5)ctrl_frame = tk.Frame(preview_frame)ctrl_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=5)ctrl_buttons = [("向左旋转 (90°)", lambda: self.transform_image('rotate_left')),("向右旋转 (90°)", lambda: self.transform_image('rotate_right')),("水平翻转", lambda: self.transform_image('flip_h')),("垂直翻转", lambda: self.transform_image('flip_v')),("重置", self.reset_image),("批量重命名", self.batch_rename_images)  # 添加按钮]for text, cmd in ctrl_buttons:btn = tk.Button(ctrl_frame, text=text, command=cmd, width=14, font=("Times New Roman", 12))btn.pack(fill=tk.X, pady=3)self.img_count_label = tk.Label(ctrl_frame, text="已添加 0 张图片", font=("Times New Roman", 12), anchor='w')self.img_count_label.pack(side=tk.BOTTOM, pady=5, fill=tk.X)self.progress = ttk.Progressbar(self.root, mode="determinate")self.progress.pack(fill=tk.X, padx=5, pady=5)def on_key_up(self, event):self.move_up()def on_key_down(self, event):self.move_down()def on_key_delete(self, event):self.remove_selected()def add_images(self):files = filedialog.askopenfilenames(filetypes=[("Image files", "*.png *.jpg *.jpeg *.bmp *.gif *.tiff")],initialdir=self.last_dir)if files:for f in files:if f not in self.image_paths:self.image_paths.append(f)self.lst_images.insert(tk.END, os.path.basename(f))self.img_count_label.config(text=f"已添加 {len(self.image_paths)} 张图片")self.lst_images.select_set(0)self.update_preview()def remove_selected(self):selected = self.lst_images.curselection()for i in reversed(selected):del self.image_paths[i]self.lst_images.delete(i)self.img_count_label.config(text=f"已添加 {len(self.image_paths)} 张图片")self.preview_label.config(image='', text='图片预览')def move_up(self):i = self.lst_images.curselection()if i and i[0] > 0:idx = i[0]self.image_paths[idx - 1], self.image_paths[idx] = self.image_paths[idx], self.image_paths[idx - 1]self.lst_images.delete(idx)self.lst_images.insert(idx - 1, os.path.basename(self.image_paths[idx - 1]))self.lst_images.select_set(idx - 1)self.update_preview()def move_down(self):i = self.lst_images.curselection()if i and i[0] < len(self.image_paths) - 1:idx = i[0]self.image_paths[idx + 1], self.image_paths[idx] = self.image_paths[idx], self.image_paths[idx + 1]self.lst_images.delete(idx)self.lst_images.insert(idx + 1, os.path.basename(self.image_paths[idx + 1]))self.lst_images.select_set(idx + 1)self.update_preview()def clear_list(self):self.image_paths.clear()self.lst_images.delete(0, tk.END)self.modified_images.clear()self.preview_label.config(image='', text='图片预览')self.img_count_label.config(text="已添加 0 张图片")def update_preview(self, event=None):if not self.lst_images.curselection():returnidx = self.lst_images.curselection()[0]path = self.image_paths[idx]img = self.modified_images.get(path, Image.open(path))img.thumbnail((300, 300))self.tkimg = ImageTk.PhotoImage(img)self.preview_label.config(image=self.tkimg, text='')def transform_image(self, action):idxs = self.lst_images.curselection()if not idxs:returnidx = idxs[0]path = self.image_paths[idx]img = self.modified_images.get(path, Image.open(path))if action == 'rotate_left':img = img.rotate(90, expand=True)elif action == 'rotate_right':img = img.rotate(-90, expand=True)elif action == 'flip_h':img = img.transpose(Image.FLIP_LEFT_RIGHT)elif action == 'flip_v':img = img.transpose(Image.FLIP_TOP_BOTTOM)self.modified_images[path] = imgself.update_preview()def reset_image(self):idx = self.lst_images.curselection()if not idx:returnpath = self.image_paths[idx[0]]self.modified_images.pop(path, None)self.update_preview()def batch_rename_images(self):if not self.image_paths:messagebox.showwarning("警告", "没有图片可重命名")returntemplate = simpledialog.askstring("命名模板", "请输入命名模板(如 Page_##.jpg):", initialvalue="Page_##.jpg")if not template or "##" not in template:messagebox.showwarning("格式错误", "命名模板必须包含 ## 作为编号占位符")returnif os.path.exists(self.renamed_dir):shutil.rmtree(self.renamed_dir)os.makedirs(self.renamed_dir, exist_ok=True)new_paths = []for i, path in enumerate(self.image_paths):new_name = template.replace("##", f"{i+1:02}")new_path = os.path.join(self.renamed_dir, new_name)shutil.copy2(path, new_path)new_paths.append(new_path)self.image_paths = new_pathsself.lst_images.delete(0, tk.END)for path in self.image_paths:self.lst_images.insert(tk.END, os.path.basename(path))self.lst_images.select_set(0)self.update_preview()messagebox.showinfo("完成", f"已重命名图片,保存在: {self.renamed_dir}")def convert_to_pdf(self):if not self.image_paths:messagebox.showwarning("警告", "没有图片可转换")returnoutput_path = filedialog.asksaveasfilename(defaultextension=".pdf", filetypes=[("PDF文件", "*.pdf")])if not output_path:returndef task():images = []for i, path in enumerate(self.image_paths):img = self.modified_images.get(path, Image.open(path))if img.mode != 'RGB':img = img.convert('RGB')images.append(img)self.progress['value'] = int((i + 1) / len(self.image_paths) * 100)self.root.update_idletasks()if images:images[0].save(output_path, save_all=True, append_images=images[1:])messagebox.showinfo("成功", f"PDF已保存至: {output_path}")self.progress['value'] = 0threading.Thread(target=task).start()
if __name__ == '__main__':root = tk.Tk()app = ImageToPDFApp(root)root.mainloop()

五、Python开发项目的优势

采用Python开发出的软件具有多重优势如下:

跨平台:Python支持Windows、macOS、Linux,代码无需修改即可兼容多系统,同时免费;

丰富的生态:Pillow、Tkinter、shutil 等库直接支持图像处理、GUI与文件操作,无需自行封装底层逻辑;

快速迭代:Python代码结构清晰,便于快速开发与后期维护,可以进行个性化扩展,满足多种多样的需求;

大语言模型友好:大语言模型可以充分网上丰富的资源,对于软件的设计、开发给出建议,辅助生成功能代码。

六、学后总结

1. 在ChatGPT的辅助下编写一个简洁高效的个性化图片转PDF工具,不仅解决了实际办公与学习场景中的具体问题,也展示了Python在图形界面应用、批处理和跨平台工具开发中的巨大潜力。

2. 此工具可能根据个人实际的需要,扩展添加如OCR识别、水印添加、图片压缩、图片放大等模块,使其逐步成长为一款专业的文档图像工具箱。

http://www.lqws.cn/news/469081.html

相关文章:

  • 从0开始学习R语言--Day28--高维回归
  • 大学专业解读——电气,自动化,仪器
  • ZooKeeper 3.9.2 集群安装指南
  • AIGC工具平台-Duix.Heygem音频对口型数字人
  • API网关Apisix管理接口速查
  • Mac电脑-触摸板增强工具-BetterTouchTool
  • SpringAI1.0.0 入门案例
  • LLM:重构数字世界的“智能操作系统”
  • 71、单元测试-Junit5简介
  • Transformer架构每层详解【代码实现】
  • 使用Trae编辑器与MCP协议构建高德地图定制化服务
  • 【unity】批量剔除图片四周空白像素的工具
  • 深入Java大厂面试:从Spring框架到微服务架构的技术解析
  • python web开发-Flask数据库集成
  • 深度剖析 PACK_SESSIONID 实现原理与安全突破机制
  • 分组交换比报文交换的传输时延更低
  • 深入剖析Linux epoll模型:从LT/ET模式到EPOLLONESHOT的实战指南
  • 【Linux】线程概念 分页式存储 优缺点
  • 开源Blazor界面组件库:Ant Design Blazor
  • 【全开源】填表问卷统计预约打卡表单系统+uniapp前端
  • ESP32 ESP-IDF Ubuntu平台工具链的标准设置
  • 百度萝卜快跑携4颗禾赛激光雷达进军迪拜,千辆L4无人车开启全球化战略
  • 华为云Flexus+DeepSeek征文 | AingDesk 对接华为云 ModelArts Studio 全流程教程与性能测评对比
  • 基于 Flutter+Sqllite 实现大学个人课表助手 APP(期末作业)
  • 【Docker 08】Compose - 容器编排
  • 【AGI】突破感知-决策边界:VLA-具身智能2.0
  • Node.js特训专栏-实战进阶:5. Express路由系统设计与优化
  • [幻灯片]分析设计高阶-02结构05-202506更新-GJ-002
  • 【Memory协议栈】Autosar架构下如何测量Fee的切页时间
  • Qthread应用