Python小工具之PDF合并
1、Python小工具(PDF合并代码)
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
from PyPDF2 import PdfMerger,PdfReader
from threading import Thread
from PIL import Image, ImageTk
class PDFMergerGUI:
def __init__(self, master):
self.master = master
master.title("PDF合并工具 v2.0")
master.geometry("800x600")
# 设置窗口图标
self.set_window_icon()
# 创建界面元素
self.create_widgets()
# 初始化变量
self.files = []
self.merging = False
def create_widgets(self):
# 文件列表框架
list_frame = ttk.LabelFrame(self.master, text="待合并文件列表")
list_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
# 文件列表
self.tree = ttk.Treeview(
list_frame,
columns=("path", "pages"),
show="headings",
selectmode="extended"
)
self.tree.heading("path", text="文件路径")
self.tree.heading("pages", text="页数")
self.tree.column("path", width=500)
self.tree.column("pages", width=80)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 滚动条
scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.tree.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.tree.configure(yscrollcommand=scrollbar.set)
# 操作按钮框架
btn_frame = ttk.Frame(self.master)
btn_frame.pack(pady=5, fill=tk.X)
ttk.Button(btn_frame, text="添加文件", command=self.add_files).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="移除选中", command=self.remove_files).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="上移", command=lambda: self.move_items(-1)).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="下移", command=lambda: self.move_items(1)).pack(side=tk.LEFT, padx=5)
# 输出路径框架
output_frame = ttk.Frame(self.master)
output_frame.pack(pady=5, fill=tk.X, padx=10)
ttk.Label(output_frame, text="输出文件:").pack(side=tk.LEFT)
self.output_entry = ttk.Entry(output_frame, width=50)
self.output_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
ttk.Button(output_frame, text="浏览...", command=self.select_output).pack(side=tk.LEFT)
# 合并按钮
self.merge_btn = ttk.Button(
self.master,
text="开始合并",
command=self.start_merge,
style="Accent.TButton"
)
self.merge_btn.pack(pady=10)
# 状态栏
self.status = ttk.Label(self.master, text="就绪", relief=tk.SUNKEN)
self.status.pack(side=tk.BOTTOM, fill=tk.X)
def setup_drag_drop(self):
# 支持拖放文件
self.master.drop_target_register('*')
self.master.dnd_bind('<<Drop>>', self.handle_drop)
def handle_drop(self, event):
files = self.master.tk.splitlist(event.data)
self.add_files(files=[f for f in files if f.lower().endswith('.pdf')])
def add_files(self, files=None):
if not files:
files = filedialog.askopenfilenames(
filetypes=[("PDF文件", "*.pdf")],
title="选择要合并的PDF文件"
)
for f in files:
if f not in self.files:
self.files.append(f)
self.tree.insert("", tk.END, values=(f, self.get_page_count(f)))
self.update_status()
def remove_files(self):
selected = self.tree.selection()
for item in selected:
index = self.tree.index(item)
del self.files[index]
self.tree.delete(item)
self.update_status()
def move_items(self, direction):
selected = self.tree.selection()
if not selected:
return
items = [(self.tree.index(item), item) for item in selected]
items.sort(reverse=direction > 0)
for index, item in items:
new_pos = index + direction
if 0 <= new_pos < len(self.files):
self.files.insert(new_pos, self.files.pop(index))
self.tree.move(item, "", new_pos)
def select_output(self):
filename = filedialog.asksaveasfilename(
defaultextension=".pdf",
filetypes=[("PDF文件", "*.pdf")],
initialfile="merged.pdf" # 添加默认文件名
)
if filename:
filename = os.path.abspath(filename) # 转为绝对路径
self.output_entry.delete(0, tk.END)
self.output_entry.insert(0, filename)
def get_page_count(self, filepath):
"""获取PDF页数(带详细错误分类)"""
try:
with open(filepath, 'rb') as f:
reader = PdfReader(f)
if reader.is_encrypted:
return ("🔒 加密", "orange") # 返回元组(显示文本,标签样式)
return (f"{len(reader.pages)}页")
except PermissionError:
return ("⛔ 无权限", "red")
except Exception as e:
print(f"页数获取错误 ({filepath}): {str(e)}")
return ("❌ 错误", "red")
def start_merge(self):
if self.merging:
return
output_path = self.output_entry.get()
if not output_path:
messagebox.showerror("错误", "请先选择输出路径")
return
if len(self.files) < 1:
messagebox.showerror("错误", "请添加要合并的PDF文件")
return
def merge_thread():
try:
merger = PdfMerger()
total = len(self.files)
for i, f in enumerate(self.files, 1):
if not os.path.exists(f):
raise FileNotFoundError(f"文件不存在: {f}")
merger.append(f)
self.update_progress(i, total)
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, 'wb') as f:
merger.write(f)
messagebox.showinfo("成功", f"合并完成!\n输出文件: {output_path}")
except Exception as e:
messagebox.showerror("错误", str(e))
finally:
merger.close()
self.merging = False
self.merge_btn.config(text="开始合并")
self.status.config(text="就绪")
self.merging = True
self.merge_btn.config(text="合并中...")
Thread(target=merge_thread, daemon=True).start()
def update_progress(self, current, total):
self.master.after(0, lambda: self.status.config(
text=f"正在合并 ({current}/{total}): {os.path.basename(self.files[current - 1])}"
))
def update_status(self):
self.status.config(text=f"已选择 {len(self.files)} 个PDF文件")
def set_window_icon(self):
"""设置窗口图标(支持PNG和ICO)"""
try:
# 优先尝试加载PNG
img = Image.open("pdf.ico")
photo = ImageTk.PhotoImage(img)
self.master.iconphoto(True, photo)
except Exception as e:
print(f"PNG图标加载失败: {str(e)}")
try:
# 备用ICO加载
self.master.iconbitmap("pdf.ico")
except Exception as e:
print(f"ICO图标加载失败: {str(e)}")
messagebox.showwarning("图标错误", "无法加载程序图标")
if __name__ == "__main__":
root = tk.Tk()
style = ttk.Style(root)
style.theme_use("clam")
app = PDFMergerGUI(root)
root.mainloop()
2、打包代码生成exe文件
pyinstaller --onefile --icon=pdf.ico --add-data="pdf.ico;." merge_pdf.py
3、打开小工具查看效果
4、小工具下载链接
下载: 请点击这里