使用Python打造高效的PDF文件管理应用(合并以及分割)

在日常工作和学习中,我们经常需要处理大量PDF文件。手动合并、分割PDF不仅耗时,还容易出错。今天,我们将使用Python的wxPython和PyMuPDF库,开发一个强大且易用的PDF文件管理工具。
C:\pythoncode\new\mergeAndsplitPdf.py

所有代码

import os
import wx
import fitz  # PyMuPDF

class PDFManagerApp(wx.Frame):
    def __init__(self):
        super().__init__(parent=None, title='PDF文件管理器')
        
        # 主面板
        panel = wx.Panel(self)
        
        # 垂直布局
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        
        # 文件夹选择行
        folder_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.folder_path = wx.TextCtrl(panel, style=wx.TE_READONLY)
        select_folder_btn = wx.Button(panel, label='选择文件夹')
        select_folder_btn.Bind(wx.EVT_BUTTON, self.on_select_folder)
        
        folder_sizer.Add(self.folder_path, proportion=1, flag=wx.EXPAND|wx.ALL, border=5)
        folder_sizer.Add(select_folder_btn, flag=wx.ALL, border=5)
        
        # PDF文件列表
        self.pdf_list = wx.ListBox(panel, style=wx.LB_MULTIPLE)
        
        # 操作按钮行
        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
        merge_btn = wx.Button(panel, label='合并PDF')
        split_btn = wx.Button(panel, label='分割PDF')
        
        merge_btn.Bind(wx.EVT_BUTTON, self.merge_pdfs)
        split_btn.Bind(wx.EVT_BUTTON, self.split_pdfs)
        
        btn_sizer.Add(merge_btn, flag=wx.ALL, border=5)
        btn_sizer.Add(split_btn, flag=wx.ALL, border=5)
        
        # 将所有部件添加到主布局
        main_sizer.Add(folder_sizer, flag=wx.EXPAND)
        main_sizer.Add(self.pdf_list, proportion=1, flag=wx.EXPAND|wx.ALL, border=5)
        main_sizer.Add(btn_sizer, flag=wx.CENTER)
        
        panel.SetSizer(main_sizer)
        
        # 设置窗口大小和居中
        self.SetSize((500, 600))
        self.Centre()

    def on_select_folder(self, event):
        """选择文件夹并列出PDF文件"""
        with wx.DirDialog(self, "选择包含PDF文件的文件夹") as dlg:
            if dlg.ShowModal() == wx.ID_OK:
                folder_path = dlg.GetPath()
                self.folder_path.SetValue(folder_path)
                
                # 清空并重新加载PDF文件列表
                self.pdf_list.Clear()
                
                # 获取文件夹中的所有PDF文件
                pdf_files = [f for f in os.listdir(folder_path) if f.lower().endswith('.pdf')]
                pdf_files.sort()  # 按文件名排序
                
                # 添加到列表框
                for pdf in pdf_files:
                    self.pdf_list.Append(pdf)

    def merge_pdfs(self, event):
        """合并选中的PDF文件"""
        # 获取选中的文件索引
        selections = self.pdf_list.GetSelections()
        
        if not selections:
            wx.MessageBox('请先选择要合并的PDF文件', '提示', wx.OK | wx.ICON_INFORMATION)
            return
        
        # 准备保存文件对话框
        with wx.FileDialog(self, "保存合并后的PDF", wildcard="PDF files (*.pdf)|*.pdf",
                           style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as saveDialog:
            if saveDialog.ShowModal() == wx.ID_OK:
                output_path = saveDialog.GetPath()
                
                # 获取文件夹路径和选中的文件名
                folder_path = self.folder_path.GetValue()
                selected_files = [self.pdf_list.GetString(idx) for idx in selections]
                
                try:
                    # 打开所有PDF
                    pdf_docs = [fitz.open(os.path.join(folder_path, f)) for f in selected_files]
                    
                    # 创建一个新的PDF文档
                    merged_pdf = fitz.open()
                    
                    # 逐个合并文档
                    for doc in pdf_docs:
                        merged_pdf.insert_pdf(doc)
                    
                    # 保存合并后的PDF
                    merged_pdf.save(output_path)
                    merged_pdf.close()
                    
                    # 关闭所有打开的文档
                    for doc in pdf_docs:
                        doc.close()
                    
                    wx.MessageBox('PDF文件合并成功!', '成功', wx.OK | wx.ICON_INFORMATION)
                
                except Exception as e:
                    wx.MessageBox(f'合并PDF时发生错误:{str(e)}', '错误', wx.OK | wx.ICON_ERROR)

    def split_pdfs(self, event):
        """将选中的PDF文件按页分割"""
        # 获取选中的文件索引
        selections = self.pdf_list.GetSelections()
        
        if not selections:
            wx.MessageBox('请先选择要分割的PDF文件', '提示', wx.OK | wx.ICON_INFORMATION)
            return
        
        # 选择输出文件夹
        with wx.DirDialog(self, "选择PDF页面输出文件夹") as dlg:
            if dlg.ShowModal() == wx.ID_OK:
                output_folder = dlg.GetPath()
                
                # 获取文件夹路径和选中的文件名
                folder_path = self.folder_path.GetValue()
                selected_files = [self.pdf_list.GetString(idx) for idx in selections]
                
                try:
                    # 处理每个选中的PDF文件
                    for filename in selected_files:
                        input_path = os.path.join(folder_path, filename)
                        pdf_doc = fitz.open(input_path)
                        
                        # 为每一页创建单独的PDF
                        for page_num in range(len(pdf_doc)):
                            # 创建只包含当前页的新PDF
                            single_page_pdf = fitz.open()
                            single_page_pdf.insert_pdf(pdf_doc, from_page=page_num, to_page=page_num)
                            
                            # 生成输出文件名
                            base_name = os.path.splitext(filename)[0]
                            output_filename = f"{base_name}_page_{page_num+1}.pdf"
                            output_path = os.path.join(output_folder, output_filename)
                            
                            # 保存单页PDF
                            single_page_pdf.save(output_path)
                            single_page_pdf.close()
                        
                        pdf_doc.close()
                    
                    wx.MessageBox('PDF文件已成功分割!', '成功', wx.OK | wx.ICON_INFORMATION)
                
                except Exception as e:
                    wx.MessageBox(f'分割PDF时发生错误:{str(e)}', '错误', wx.OK | wx.ICON_ERROR)

def main():
    app = wx.App()
    frame = PDFManagerApp()
    frame.Show()
    app.MainLoop()

if __name__ == '__main__':
    main()

项目背景

随着数字文档的普及,PDF已成为最常用的文档格式之一。然而,对PDF文件的管理和处理往往让人感到繁琐。市面上的PDF工具要么功能单一,要么操作复杂,价格高昂。

技术选型

我们选择了以下技术栈:

  • wxPython:跨平台的GUI框架,提供原生的用户界面体验
  • PyMuPDF:高性能的PDF处理库,支持文档合并、分割等操作
  • Python:简洁且强大的编程语言

核心功能

1. 文件夹选择与PDF列表

应用程序提供了直观的文件夹选择功能。用户只需点击"选择文件夹"按钮,即可浏览并选择包含PDF文件的目录。所有PDF文件将按文件名自动排序显示在列表中。

def on_select_folder(self, event):
    with wx.DirDialog(self, "选择包含PDF文件的文件夹") as dlg:
        if dlg.ShowModal() == wx.ID_OK:
            folder_path = dlg.GetPath()
            # 获取并排序PDF文件列表
            pdf_files = [f for f in os.listdir(folder_path) if f.lower().endswith('.pdf')]
            pdf_files.sort()

2. PDF文件合并

再也不用担心手动合并多个PDF文件了!我们的应用支持多文件选择和快速合并。用户可以:

  • 选择多个PDF文件
  • 点击"合并PDF"按钮
  • 选择合并后文件的保存位置

核心合并逻辑如下:

def merge_pdfs(self, event):
    # 获取选中的文件
    selected_files = [self.pdf_list.GetString(idx) for idx in selections]
    
    # 使用PyMuPDF合并PDF
    pdf_docs = [fitz.open(os.path.join(folder_path, f)) for f in selected_files]
    merged_pdf = fitz.open()
    
    for doc in pdf_docs:
        merged_pdf.insert_pdf(doc)
    
    merged_pdf.save(output_path)

3. PDF文件分割

需要将一个PDF文件按页拆分?轻松搞定!

  • 选择要分割的PDF文件
  • 点击"分割PDF"按钮
  • 选择输出文件夹
  • 应用程序将自动为每一页生成独立的PDF文件

分割实现代码:

def split_pdfs(self, event):
    for filename in selected_files:
        pdf_doc = fitz.open(input_path)
        
        for page_num in range(len(pdf_doc)):
            # 创建单页PDF
            single_page_pdf = fitz.open()
            single_page_pdf.insert_pdf(pdf_doc, from_page=page_num, to_page=page_num)
            
            # 保存单页PDF
            output_filename = f"{base_name}_page_{page_num+1}.pdf"
            single_page_pdf.save(output_path)

使用指南

环境准备

安装必要的依赖:

pip install wxPython pymupdf

运行应用

  1. 克隆项目代码
  2. 执行 python mergeAndsplitPdf.py
  3. 选择文件夹,开始管理您的PDF文件!

运行结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/938771.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习中自适应学习率调度器

传统观点认为,太大的学习率不利于优化深度神经网络,而相比固定的学习率而言,变化的学习率更能提供快速的收敛。基于此,本文作者基于理论基础提出了一个计算深度神经网络学习率的新方法。实验结果证明了该方法的有效性。 训练神经…

文献研读|基于像素语义层面图像重建的AI生成图像检测

前言:本篇文章主要对基于重建的AI生成图像检测的四篇相关工作进行介绍,分别为基于像素层面重建的检测方法 DIRE 和 Aeroblade,以及基于语义层面重建的检测方法 SimGIR 和 Zerofake;并对相应方法进行比较。 相关文章:论…

ElasticSearch06-分片节点分配

零、文章目录 ElasticSearch06-分片节点分配 1、单节点多分片多副本 (1)启动一个空节点 节点的配置如下 cluster.name: mycluster node.name: node-01 node.master: true node.data: true network.host: 127.0.0.1 http.port: 9201 transport.tcp.p…

信息学奥赛一本通 1438:灯泡 | 洛谷 P5931 [清华集训2015] 灯泡

【题目链接】 ybt 1438:灯泡 洛谷 P5931 [清华集训2015] 灯泡 【题目考点】 1. 三分 求函数极值 2. 相似三角形 3. 对钩函数 【解题思路】 首先考虑影子还没有到达对面墙壁的情况 记BM长度为x,影子为AM,长度为L。三角形ABC相似于三角…

揭开 Choerodon UI 拖拽功能的神秘面纱

01 引言 系统的交互方式主要由点击、选择等组成。为了提升 HZERO 系统的用户体验、减少部分操作步骤,组件库集成了卓越的拖拽功能,让用户可以更高效流畅的操作系统。 例如:表格支持多行拖拽排序、跨表数据调整、个性化调整列顺序&#xff1…

【物联网技术与应用】实验4:继电器实验

实验4 继电器实验 【实验介绍】 继电器是一种用于响应施加的输入信号而在两个或多个点或设备之间提供连接的设备。换句话说,继电器提供了控制器和设备之间的隔离,因为设备可以在AC和DC上工作。但是,他们从微控制器接收信号,因此…

fpga系列 HDL:Quartus II 时序约束 静态时序分析 (STA) test.out.sdc的文件结构

test.out.sdc的文件结构 ## Generated SDC file "test.out.sdc"## Copyright (C) 1991-2013 Altera Corporation ## Your use of Altera Corporations design tools, logic functions ## and other software and tools, and its AMPP partner logic ## functions,…

Windows安全中心(病毒和威胁防护)的注册

文章目录 Windows安全中心(病毒和威胁防护)的注册1. 简介2. WSC注册初探3. WSC注册原理分析4. 关于AMPPL5. 参考 Windows安全中心(病毒和威胁防护)的注册 本文我们来分析一下Windows安全中心(Windows Security Center…

HTML中的Vue3解析!

#Vue 3 是一个用于构建用户界面的渐进式 JavaScript 框架。它在 HTML 中发挥着重要的作用,可以让开发者轻松地创建交互式的网页应用。与 HTML 结合时,Vue 3 通过自定义指令、组件等方式增强了 HTML 的功能。# 一、vue的概述 Vue 采用了双向数据绑定机制…

ARM嵌入式学习--第八天(PWM)

PWM -PWM介绍 PWM(pulse Width Modulation)简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在测量,通信,工控等方面 PWM的频率 是指在1秒钟内,信号从…

TongESB7.1.0.0安装参考指引+测试参考(by lqw)

文章目录 安装安装准备配置jdk安装管理中心(manager)安装运行时(server)安装mysql并配置manager(新装阶段考虑)放入授权启动内置redis启动内置redis启动manager和server停止manager和server访问控制台如何在控制台上重置密码 测试…

【现代C++开发】使用现代的C++快速开发一款串口读写软件

文章目录 前言一、必要条件二、实现步骤1.创建项目2.配置代码提示3.安装依赖3.编译程序4. 编写实现代码 前言 最近关于C闹出来的动静态势挺大的,主要是由于爱美丽卡开始抵制C,最近有不少文章都报道了这件事,比如 即使C到了这个时候&#xf…

linux上qt打包(二)

sudo apt install git 新建一个文件夹 名为xiazai, chmod -R 777 xiazai cd xiazai 并进入这个文件夹,然后clone git clone https://github.com/probonopd/linuxdeployqt.git 此处可能要fanQiang才能下 cd linuxdeployqt文件夹 下载平台需要的…

电脑开机提示error loading operating system怎么修复?

前一天电脑还能正常运行,但今天启动时却显示“Error loading operating system”(加载操作系统错误)。我已经仔细检查了硬盘、接线、内存、CPU和电源,确认这些硬件都没有问题。硬盘在其他电脑上可以正常使用,说明不是硬…

Nginx 在不同操作系统下的安装指南

Nginx 在不同操作系统下的安装指南 一、Linux 系统下 Nginx 的安装 (一)基于 Ubuntu 系统 更新软件包列表 打开终端,首先执行sudo apt-get update命令。这一步是为了确保系统的软件包列表是最新的,能够获取到最新版本的 Nginx 及…

NTLMv2 离线爆破

攻击者(kali):192.168.72.162 受害者(administrator):192.168.72.163 因为 NTLM 身份验证是通过计算正确的挑战值得出的,所以如果我们能获取域用户的 NTLM 认证某一服务的 Net-NTLM v2 Hash …

“TA”说|表数据备份还原:SQLark 百灵连接助力项目部署验收

💬 南飞雁|应用开发工程师 有些重要项目的部署验收,会在生产环境完成,验收完成后,又需要把这部分数据清空。这时就需要对数据表进行备份和还原,虽然可以通过命令直接实现,但是有一些操作门槛&am…

C++动态规划解决最长公共子序列

动规非常经典的一道题目,由于需要用到二维数组——姑且算为中等难度的题目,其实和01背包有着极高的相似度,无论是实现还是理论。 今天这篇博客不讲过多的DP理论,重在讲解题目本身。其实有一定经验的同志都清楚,DP的难点…

Meta升级Ray-Ban智能眼镜:新增实时AI对话与翻译功能

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

visual studio添加滚动条预览

如何在vs中添加如图的滚动条呢? 打开VS 工具栏 选项 - 文本编辑器 - C/C - 滚动条 行为-使用缩略图 -- 确定