Python借助AI快速開發(fā)圖片轉(zhuǎn)PDF的工具
一、開發(fā)背景與適用場景
隨著數(shù)字文檔處理需求的激增,圖片轉(zhuǎn)PDF的需求日益廣泛。從學生提交圖像化作業(yè),到教師整合掃描試卷等資料,再到行政人員歸檔證件照片,工作中的方方面面都離不開圖片的處理。如何高效、批量地將多個圖片文件整理為一個標準PDF文件,成為日常辦公中的痛點之一。
盡管市面上已有諸多工具支持圖像轉(zhuǎn)PDF,但大多數(shù)要么受限于平臺(如僅支持Windows或Web)、要么操作繁瑣、功能單一。而借助Python強大的圖像處理與GUI庫,我們可以快速構(gòu)建一款輕量級、跨平臺、功能豐富的圖片轉(zhuǎn)PDF工具,適用于:
- 教育場景下學生作業(yè)或試卷掃描;
- 辦公場景下的證件照片批量歸檔;
- 圖片編輯后統(tǒng)一格式輸出;
- 快速制作圖文資料手冊等。
軟件的主界面
二、主要功能與技術(shù)特點
本項目以 Tkinter 為GUI主框架,集成 PIL (Pillow) 處理圖像、threading 異步轉(zhuǎn)PDF、shutil 文件管理等模塊,具備如下功能:
1. 多圖管理與預(yù)覽
- 支持批量添加、刪除、清空圖片;
- 支持上下移動圖片列表中的文件排序,決定PDF頁序;
- 實時圖片預(yù)覽,并在右側(cè)展示縮略圖;
2. 圖片編輯操作
- 支持圖片的 90° 左右旋轉(zhuǎn);
- 支持水平 / 垂直翻轉(zhuǎn);
- 支持單張圖片的“重置”功能,回到原始狀態(tài);
- 所有圖像操作均在不影響原圖基礎(chǔ)上完成,保留數(shù)據(jù)完整性;
3. 文件命名與批處理
- 用戶可輸入模板(如“Page_##.jpg”)對所有圖片批量重命名; 比如你們把Page改為作業(yè)_##.jpg,生成的圖片就變成作業(yè)_01.jpg, 作業(yè)_02.jpg,作業(yè)_03.jpg
- 自動創(chuàng)建名為
renamed_images/
的目錄并保存副本,確保原圖不被覆蓋;
圖片重命名
4. PDF轉(zhuǎn)換與保存
一鍵將所有圖片按設(shè)定順序合成并轉(zhuǎn)化為PDF文件;
異步轉(zhuǎn)換不阻塞界面響應(yīng),轉(zhuǎn)化速度高;
支持軟件底部進度條反饋轉(zhuǎn)換進度;
自動處理圖像色彩模式為PDF兼容的RGB格式;
5. 人性化交互設(shè)計
鍵盤快捷操作支持:向上 / 向下鍵調(diào)整順序,Delete鍵刪除選中;
彈窗提示操作狀態(tài),如保存路徑、錯誤提示、任務(wù)完成反饋等。
四、開發(fā)過程
本次開發(fā)沒有像以往一樣直接給出提示詞,而是根據(jù)微信群中網(wǎng)友上傳的一段采用pyqt5編寫的代碼加工而成。原軟件樣式:
我給出的提示詞如下,主要功能是把這個pyqt5框架的代碼轉(zhuǎn)化為tkinter框架,這樣不用安裝pyqt5這個模塊就可以運行軟件,而且打包后的軟件可能個頭小點,但損失的可能是界面的美觀性。
轉(zhuǎn)換完成,并成功運行后,我又把新代碼交給AI,讓它給我一些可以添加功能的建議
AI給出了批量重命名的功能建議。
最終確定添加批量重命名功能和用鍵盤上下鍵來切換顯示預(yù)覽圖片。用delete鍵來控制圖片刪除功能。
五、使用方法與操作流程
1. 啟動程序
運行腳本后,程序?qū)⒆詣訂右粋€800×600的窗口,主界面分為頂部按鈕欄、左側(cè)列表框和右側(cè)預(yù)覽區(qū)。
2. 添加與管理圖片
點擊“添加圖片”可選取多張圖片文件導(dǎo)入,隨后可在列表中拖動排序,或使用“上移 / 下移”按鈕調(diào)整順序。
3. 圖像編輯與重命名
選中某張圖片后,可以在右側(cè)操作區(qū)執(zhí)行旋轉(zhuǎn)、翻轉(zhuǎn)等編輯操作。如需統(tǒng)一重命名圖片,點擊“批量重命名”,輸入模板后自動保存副本到 renamed_images/ 文件夾中。
4. 轉(zhuǎn)換與保存PDF
點擊“轉(zhuǎn)換為PDF”,選擇保存位置后,程序?qū)⒆詣訄?zhí)行圖像轉(zhuǎn)換、拼接與導(dǎo)出過程。用戶可實時查看進度條反饋。
六、代碼展示
本項目采用類的寫法,主要運用了Python的自帶的標準庫如os,shutil等,完整的代碼如下:
import os import shutil import threading import tkinter as tk from tkinter import filedialog, messagebox, simpledialog, ttk from PIL import Image, ImageTk class ImageToPDFApp: def __init__(self, root): self.root = root self.root.title("圖片轉(zhuǎn)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), ("轉(zhuǎn)換為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="圖片預(yù)覽", 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 = [ ("向左旋轉(zhuǎn) (90°)", lambda: self.transform_image('rotate_left')), ("向右旋轉(zhuǎn) (90°)", lambda: self.transform_image('rotate_right')), ("水平翻轉(zhuǎn)", lambda: self.transform_image('flip_h')), ("垂直翻轉(zhuǎn)", 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='圖片預(yù)覽') 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='圖片預(yù)覽') self.img_count_label.config(text="已添加 0 張圖片") def update_preview(self, event=None): if not self.lst_images.curselection(): return idx = 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: return idx = 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] = img self.update_preview() def reset_image(self): idx = self.lst_images.curselection() if not idx: return path = 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("警告", "沒有圖片可重命名") return template = simpledialog.askstring("命名模板", "請輸入命名模板(如 Page_##.jpg):", initialvalue="Page_##.jpg") if not template or "##" not in template: messagebox.showwarning("格式錯誤", "命名模板必須包含 ## 作為編號占位符") return if 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_paths self.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("警告", "沒有圖片可轉(zhuǎn)換") return output_path = filedialog.asksaveasfilename(defaultextension=".pdf", filetypes=[("PDF文件", "*.pdf")]) if not output_path: return def 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'] = 0 threading.Thread(target=task).start() if __name__ == '__main__': root = tk.Tk() app = ImageToPDFApp(root) root.mainloop()
七、Python開發(fā)項目的優(yōu)勢
采用Python開發(fā)出的軟件具有多重優(yōu)勢如下:
跨平臺:Python支持Windows、macOS、Linux,代碼無需修改即可兼容多系統(tǒng),同時免費;
豐富的生態(tài):Pillow、Tkinter、shutil 等庫直接支持圖像處理、GUI與文件操作,無需自行封裝底層邏輯;
快速迭代:Python代碼結(jié)構(gòu)清晰,便于快速開發(fā)與后期維護,可以進行個性化擴展,滿足多種多樣的需求;
大語言模型友好:大語言模型可以充分網(wǎng)上豐富的資源,對于軟件的設(shè)計、開發(fā)給出建議,輔助生成功能代碼。
八、學后總結(jié)
1. 在AI 的輔助下編寫一個簡潔高效的個性化圖片轉(zhuǎn)PDF工具,不僅解決了實際辦公與學習場景中的具體問題,也展示了Python在圖形界面應(yīng)用、批處理和跨平臺工具開發(fā)中的巨大潛力。
2. 此工具可能根據(jù)個人實際的需要,擴展添加如OCR識別、水印添加、圖片壓縮、圖片放大等模塊,使其逐步成長為一款專業(yè)的文檔圖像工具箱。
到此這篇關(guān)于Python借助AI快速開發(fā)圖片轉(zhuǎn)PDF的工具的文章就介紹到這了,更多相關(guān)Python圖片轉(zhuǎn)pdf內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python中列表乘法和列表推導(dǎo)式的區(qū)別舉例詳解
在Python中列表是一種非常靈活和強大的數(shù)據(jù)結(jié)構(gòu),支持多種運算和操作,這篇文章主要介紹了Python中列表乘法和列表推導(dǎo)式區(qū)別的相關(guān)資料,文中通過代碼就介紹的非常詳細,需要的朋友可以參考下2025-04-04剖析Python的Tornado框架中session支持的實現(xiàn)代碼
這篇文章主要介紹了剖析Python的Tornado框架中session支持的實現(xiàn)代碼,這樣就可以使用Django等框架中大家所熟悉的session了,需要的朋友可以參考下2015-08-08Python的3種運行方式:命令行窗口、Python解釋器、IDLE的實現(xiàn)
這篇文章主要介紹了Python的3種運行方式:命令行窗口、Python解釋器、IDLE的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-10-10