PDF文件合并、删除特定页面的工具分享

发布于:2025-06-14 ⋅ 阅读:(26) ⋅ 点赞:(0)

linux 平台好像没有带图形界面的PDF处理工具。为了应付日常使用,我用豆包写了一个PDF文件合并、删除特定页面的工具。提示词如下:

用 python 编写一个PDF合并、删除特定页面的工具。要求:

- 具有可视化操作界面。

- 界面上方是一个选择PDF文件的区域。支持多选。选择文件后,将文件路径显示于下方。

- 选择文件区域下方是“开始合并”按钮。点击后开始按顺序合并PDF文件。合并完成后弹出合并后的文件已保存的提示框。

- 选择文件区域与“开始合并”按钮合属于一个操作区。与接下来的页面删除操作区要有区域分割的美化设计。

- 页面删除操作区包含一个待删除页码输入框、“删除”按钮.点击“删除”按钮后保存。

- 待删除页码输入框支持一次输入多个页码,中间用“,”隔开。这句话也在界面上提示用户。

写的很随便。也说明了现在AI 编程已经很方便了,不用刻意按某特定格式。

但是很多细节,AI 理解的不好,还需要多伦对话调整。最明显的就是整个窗口的大小,他最初设置的不对,隐藏了一些按钮。

具体代码如下:

import os
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from PyPDF2 import PdfReader, PdfWriter
import re

class PDFTool:
    def __init__(self, root):
        self.root = root
        self.root.title("PDF处理工具")
        self.root.geometry("700x600")
        self.root.resizable(True, True)
        
        # 设置中文字体支持
        self.root.option_add("*Font", "SimHei 10")
        
        # 存储选择的PDF文件路径
        self.pdf_files = []
        # 当前打开的PDF文件
        self.current_pdf = None
        # 当前PDF的页面数
        self.page_count = 0
        
        # 创建主框架
        self.main_frame = ttk.Frame(root, padding="10")
        self.main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建合并操作区域
        self.create_merge_frame()
        
        # 创建分隔线
        self.separator = ttk.Separator(self.main_frame, orient='horizontal')
        self.separator.pack(fill=tk.X, pady=15)
        
        # 创建删除页面操作区域
        self.create_delete_frame()
        
        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        self.status_bar = ttk.Label(root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    def create_merge_frame(self):
        """创建PDF合并操作区域"""
        merge_frame = ttk.LabelFrame(self.main_frame, text="PDF合并", padding="10")
        merge_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        
        # 选择文件按钮
        select_btn = ttk.Button(merge_frame, text="选择PDF文件", command=self.select_pdf_files)
        select_btn.pack(pady=5)
        
        # 文件列表区域
        list_frame = ttk.Frame(merge_frame)
        list_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        # 列表框显示已选择的文件
        self.file_listbox = tk.Listbox(list_frame, selectmode=tk.EXTENDED, width=80, height=5)
        self.file_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 列表框滚动条
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.file_listbox.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.file_listbox.config(yscrollcommand=scrollbar.set)
        
        # 移动文件顺序的按钮
        btn_frame = ttk.Frame(merge_frame)
        btn_frame.pack(fill=tk.X, pady=5)
        
        up_btn = ttk.Button(btn_frame, text="上移", command=self.move_up)
        up_btn.pack(side=tk.LEFT, padx=5)
        
        down_btn = ttk.Button(btn_frame, text="下移", command=self.move_down)
        down_btn.pack(side=tk.LEFT, padx=5)
        
        remove_btn = ttk.Button(btn_frame, text="移除", command=self.remove_file)
        remove_btn.pack(side=tk.LEFT, padx=5)
        
        # 合并按钮
        merge_btn = ttk.Button(merge_frame, text="开始合并", command=self.merge_pdfs)
        merge_btn.pack(pady=10)
    
    def create_delete_frame(self):
        """创建PDF页面删除操作区域"""
        delete_frame = ttk.LabelFrame(self.main_frame, text="PDF页面删除", padding="10")
        delete_frame.pack(fill=tk.BOTH, expand=True)
        
        # 选择PDF文件
        select_pdf_frame = ttk.Frame(delete_frame)
        select_pdf_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(select_pdf_frame, text="选择PDF文件:").pack(side=tk.LEFT)
        
        self.pdf_path_var = tk.StringVar()
        pdf_entry = ttk.Entry(select_pdf_frame, textvariable=self.pdf_path_var, width=50)
        pdf_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        browse_btn = ttk.Button(select_pdf_frame, text="浏览...", command=self.browse_pdf)
        browse_btn.pack(side=tk.LEFT)
        
        # 页面信息
        self.page_info_var = tk.StringVar()
        page_info_label = ttk.Label(delete_frame, textvariable=self.page_info_var)
        page_info_label.pack(fill=tk.X, pady=5)
        
        # 页码输入
        page_frame = ttk.Frame(delete_frame)
        page_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(page_frame, text="待删除页码(用逗号分隔):").pack(side=tk.LEFT)
        
        self.pages_to_delete_var = tk.StringVar()
        pages_entry = ttk.Entry(page_frame, textvariable=self.pages_to_delete_var, width=30)
        pages_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        # 提示信息
        ttk.Label(delete_frame, text="提示: 页码从1开始,例如删除第1、3、5页,请输入: 1,3,5").pack(fill=tk.X, pady=5)
        
        # 删除按钮
        self.delete_btn = ttk.Button(delete_frame, text="删除并保存", command=self.delete_and_save_pages)
        self.delete_btn.pack(pady=10)
    
    def select_pdf_files(self):
        """选择PDF文件"""
        files = filedialog.askopenfilenames(
            title="选择PDF文件",
            filetypes=[("PDF文件", "*.pdf"), ("所有文件", "*.*")]
        )
        
        if files:
            self.pdf_files.extend(files)
            self.update_file_listbox()
    
    def update_file_listbox(self):
        """更新文件列表框显示"""
        self.file_listbox.delete(0, tk.END)
        for file in self.pdf_files:
            self.file_listbox.insert(tk.END, os.path.basename(file))
    
    def move_up(self):
        """将选中的文件上移"""
        selected = self.file_listbox.curselection()
        if not selected:
            return
        
        for i in selected:
            if i > 0:
                self.pdf_files[i], self.pdf_files[i-1] = self.pdf_files[i-1], self.pdf_files[i]
        
        self.update_file_listbox()
        # 重新选择
        for i in selected:
            if i > 0:
                self.file_listbox.selection_set(i-1)
    
    def move_down(self):
        """将选中的文件下移"""
        selected = self.file_listbox.curselection()
        if not selected:
            return
        
        # 从后往前处理,避免索引变化问题
        for i in reversed(selected):
            if i < len(self.pdf_files) - 1:
                self.pdf_files[i], self.pdf_files[i+1] = self.pdf_files[i+1], self.pdf_files[i]
        
        self.update_file_listbox()
        # 重新选择
        for i in selected:
            if i < len(self.pdf_files) - 1:
                self.file_listbox.selection_set(i+1)
    
    def remove_file(self):
        """移除选中的文件"""
        selected = self.file_listbox.curselection()
        if not selected:
            return
        
        # 从后往前删除,避免索引变化问题
        for i in reversed(selected):
            del self.pdf_files[i]
        
        self.update_file_listbox()
    
    def merge_pdfs(self):
        """合并选中的PDF文件"""
        if not self.pdf_files:
            messagebox.showwarning("警告", "请先选择PDF文件")
            return
        
        # 选择保存位置
        output_path = filedialog.asksaveasfilename(
            title="保存合并后的PDF",
            defaultextension=".pdf",
            filetypes=[("PDF文件", "*.pdf"), ("所有文件", "*.*")]
        )
        
        if not output_path:
            return
        
        try:
            # 创建PDF写入器
            writer = PdfWriter()
            
            # 合并所有PDF
            for pdf_file in self.pdf_files:
                reader = PdfReader(pdf_file)
                for page in reader.pages:
                    writer.add_page(page)
            
            # 保存合并后的PDF
            with open(output_path, "wb") as output_file:
                writer.write(output_file)
            
            messagebox.showinfo("成功", f"PDF文件已合并并保存至:\n{output_path}")
            self.status_var.set(f"PDF已合并: {output_path}")
            
        except Exception as e:
            messagebox.showerror("错误", f"合并PDF时出错:\n{str(e)}")
            self.status_var.set("合并PDF失败")
    
    def browse_pdf(self):
        """浏览并选择要删除页面的PDF文件"""
        file_path = filedialog.askopenfilename(
            title="选择PDF文件",
            filetypes=[("PDF文件", "*.pdf"), ("所有文件", "*.*")]
        )
        
        if file_path:
            self.pdf_path_var.set(file_path)
            self.current_pdf = file_path
            self.load_pdf_info()
    
    def load_pdf_info(self):
        """加载PDF文件信息"""
        try:
            reader = PdfReader(self.current_pdf)
            self.page_count = len(reader.pages)
            self.page_info_var.set(f"当前PDF: {os.path.basename(self.current_pdf)}, 共 {self.page_count} 页")
            # 确保删除按钮可用
            self.delete_btn.config(state=tk.NORMAL)
        except Exception as e:
            messagebox.showerror("错误", f"加载PDF文件时出错:\n{str(e)}")
            self.page_info_var.set("加载PDF失败")
    
    def delete_and_save_pages(self):
        """删除指定页码的页面并保存"""
        if not self.current_pdf:
            messagebox.showwarning("警告", "请先选择PDF文件")
            return
        
        pages_input = self.pages_to_delete_var.get().strip()
        if not pages_input:
            messagebox.showwarning("警告", "请输入要删除的页码")
            return
        
        # 解析页码输入
        try:
            pages_to_delete = []
            for page_str in re.split(r'[,\s]+', pages_input):
                if page_str:
                    page_num = int(page_str)
                    if page_num < 1 or page_num > self.page_count:
                        raise ValueError(f"页码 {page_num} 超出范围 (1-{self.page_count})")
                    pages_to_delete.append(page_num - 1)  # 转换为0-based索引
            
            # 确保页码唯一并排序
            pages_to_delete = sorted(set(pages_to_delete))
            
            # 创建PDF读取器和写入器
            reader = PdfReader(self.current_pdf)
            writer = PdfWriter()
            
            # 添加不需要删除的页面
            for i in range(self.page_count):
                if i not in pages_to_delete:
                    writer.add_page(reader.pages[i])
            
            # 选择保存位置
            output_path = filedialog.asksaveasfilename(
                title="保存修改后的PDF",
                defaultextension=".pdf",
                filetypes=[("PDF文件", "*.pdf"), ("所有文件", "*.*")]
            )
            
            if not output_path:
                return
            
            # 保存修改后的PDF
            with open(output_path, "wb") as output_file:
                writer.write(output_file)
            
            messagebox.showinfo("成功", f"已删除 {len(pages_to_delete)} 页,共 {self.page_count - len(pages_to_delete)} 页剩余\nPDF文件已保存至:\n{output_path}")
            self.status_var.set(f"PDF已保存: {output_path}")
            
            # 更新当前PDF为修改后的版本
            self.current_pdf = output_path
            self.load_pdf_info()
            
        except ValueError as e:
            messagebox.showerror("输入错误", str(e))
        except Exception as e:
            messagebox.showerror("错误", f"处理PDF时出错:\n{str(e)}")

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

 


网站公告

今日签到

点亮在社区的每一天
去签到