Python-pdf工具自制(合并、拆分、删除)

news2024/11/19 19:34:47

pdf工具,之前写的合并工具有点麻烦,使用PyQt5库重写合并拆分和删除指定页面的程序

实现如图:

代码:

import sys

import os

from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QFileDialog, QListWidget, QMessageBox, QLineEdit, QHBoxLayout, QInputDialog

from PyQt5.QtCore import Qt, QThread, pyqtSignal

from PyPDF2 import PdfReader, PdfWriter, PdfMerger

from PyQt5 import QtGui



class CustomListWidget(QListWidget):

    def __init__(self, parent=None):

        super().__init__(parent)



class Worker(QThread):

    finished = pyqtSignal(str)

    error = pyqtSignal(str)



    def __init__(self, pdf_files, range_str=None, save_path=None, operation=None):

        super().__init__()

        self.pdf_files = pdf_files

        self.range_str = range_str

        self.save_path = save_path

        self.operation = operation



    def run(self):

        try:

            if self.operation == 'merge':

                merger = PdfMerger()

                for pdf in self.pdf_files:

                    merger.append(pdf)

                merger.write(self.save_path)

                merger.close()

                self.finished.emit('PDF文件已成功合并。')

            elif self.operation == 'split':

                start_page, end_page = self.parse_range(self.range_str)

                reader = PdfReader(self.pdf_files[zxsq-anti-bbcode-0])

                os.makedirs(self.save_path, exist_ok=True)

                for page in range(start_page, end_page + 1):

                    writer = PdfWriter()

                    writer.add_page(reader.pages)

                    split_save_path = os.path.join(self.save_path, f'Page_{page + 1}.pdf')

                    writer.write(split_save_path)

                self.finished.emit('PDF文件已成功拆分并保存。')

            elif self.operation == 'delete':

                start_page, end_page = self.parse_range(self.range_str)

                reader = PdfReader(self.pdf_files[zxsq-anti-bbcode-0])

                writer = PdfWriter()

                for page_num in range(len(reader.pages)):

                    if not (start_page <= page_num <= end_page):

                        writer.add_page(reader.pages[zxsq-anti-bbcode-page_num])

                writer.write(self.save_path)

                self.finished.emit('指定页面已从PDF中删除。')

        except Exception as e:

            self.error.emit(str(e))



    def parse_range(self, range_str):

        if '-' in range_str:

            start_page, end_page = map(int, range_str.split('-'))

        else:

            start_page = end_page = int(range_str)

        return start_page - 1, end_page - 1  # Convert to 0-based index



class PDFMergerApp(QMainWindow):

    def __init__(self):

        super().__init__()

        self.initUI()

        self.pdf_files = []



    def initUI(self):

        self.setWindowTitle('PDF 工具箱')

        self.setWindowIcon(QtGui.QIcon('111.ico'))

        self.setGeometry(100, 100, 800, 600)



        mainLayout = QVBoxLayout()



        self.addButton = QPushButton('添加 PDF', self)

        self.addButton.clicked.connect(self.addPDF)

        mainLayout.addWidget(self.addButton)



        self.listWidget = CustomListWidget(self)

        mainLayout.addWidget(self.listWidget)



        # 删除按钮的水平布局

        deleteLayout = QHBoxLayout()

        self.removeButton = QPushButton('删除选定', self)

        self.removeButton.clicked.connect(self.removeSelected)

        deleteLayout.addWidget(self.removeButton)



        self.removeAllButton = QPushButton('删除全部', self)

        self.removeAllButton.clicked.connect(self.removeAll)

        deleteLayout.addWidget(self.removeAllButton)

        mainLayout.addLayout(deleteLayout)



        self.mergeButton = QPushButton('合并 PDFs', self)

        self.mergeButton.clicked.connect(self.mergePDFs)

        mainLayout.addWidget(self.mergeButton)



        # 拆分和删除页码的水平布局

        splitDeleteLayout = QHBoxLayout()



        self.splitInput = QLineEdit(self)

        self.splitInput.setPlaceholderText('输入拆分范围,如 1 或 1-4')

        splitDeleteLayout.addWidget(self.splitInput)



        self.splitButton = QPushButton('拆分 PDF', self)

        self.splitButton.clicked.connect(self.splitPDF)

        splitDeleteLayout.addWidget(self.splitButton)



        self.deleteInput = QLineEdit(self)

        self.deleteInput.setPlaceholderText('输入删除页码,如 1 或 1-4')

        splitDeleteLayout.addWidget(self.deleteInput)



        self.deleteButton = QPushButton('删除页面', self)

        self.deleteButton.clicked.connect(self.deletePages)

        splitDeleteLayout.addWidget(self.deleteButton)



        mainLayout.addLayout(splitDeleteLayout)



        container = QWidget()

        container.setLayout(mainLayout)

        self.setCentralWidget(container)



    def addPDF(self):

        files, _ = QFileDialog.getOpenFileNames(self, '打开文件', '', 'PDF files (*.pdf)')

        for file_path in files:

            self.addPDFFile(file_path)



    def addPDFFile(self, file_path):

        if file_path and file_path not in self.pdf_files:

            self.pdf_files.append(file_path)

            self.listWidget.addItem(file_path)



    def removeSelected(self):

        for item in self.listWidget.selectedItems():

            self.pdf_files.remove(item.text())

            self.listWidget.takeItem(self.listWidget.row(item))



    def removeAll(self):

        self.pdf_files.clear()

        self.listWidget.clear()



    def mergePDFs(self):

        save_path, _ = QFileDialog.getSaveFileName(self, '保存文件', '', 'PDF files (*.pdf)')

        if save_path:

            self.thread = Worker(self.pdf_files, save_path=save_path, operation='merge')

            self.thread.finished.connect(self.onFinished)

            self.thread.error.connect(self.onError)

            self.thread.start()



    def splitPDF(self):

        if len(self.pdf_files) != 1:

            QMessageBox.warning(self, "错误", "请只选择一个PDF文件进行拆分。")

            return



        range_str = self.splitInput.text().strip()

        folder_path = self.getFolderName()

        if range_str and folder_path:

            self.thread = Worker(self.pdf_files, range_str=range_str, save_path=folder_path, operation='split')

            self.thread.finished.connect(self.onFinished)

            self.thread.error.connect(self.onError)

            self.thread.start()



    def getFolderName(self):

        folder_path = QFileDialog.getExistingDirectory(self, "选择保存拆分文件的位置")

        if folder_path:

            folder_name, ok = QInputDialog.getText(self, "文件夹名称", "输入文件夹名称:")

            if ok and folder_name:

                full_path = os.path.join(folder_path, folder_name)

                os.makedirs(full_path, exist_ok=True)

                return full_path

        return None



    def deletePages(self):

        if len(self.pdf_files) != 1:

            QMessageBox.warning(self, "错误", "请只选择一个PDF文件进行删除操作。")

            return



        range_str = self.deleteInput.text().strip()

        save_path = QFileDialog.getSaveFileName(self, '保存文件', '', 'PDF files (*.pdf)')[zxsq-anti-bbcode-0]

        if save_path and range_str:

            self.thread = Worker(self.pdf_files, range_str=range_str, save_path=save_path, operation='delete')

            self.thread.finished.connect(self.onFinished)

            self.thread.error.connect(self.onError)

            self.thread.start()



    def onFinished(self, message):

        self.show_message("操作完成", message)

        self.clear_pdf_list()



    def onError(self, error_message):

        self.show_message("操作失败", error_message)



    def show_message(self, title, message):

        QMessageBox.information(self, title, message)



    def clear_pdf_list(self):

        self.pdf_files.clear()

        self.listWidget.clear()



def main():

    app = QApplication(sys.argv)

    ex = PDFMergerApp()

    ex.show()

    sys.exit(app.exec_())



if __name__ == '__main__':

    main()

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

P10 Linux进程编程 fork创建子进程

目录 前言 01 fork()创建子进程 示例 1使用 fork()创建子进程。 02 fork创建新进程时发生了什么事? 2.1 父、子进程中对应的文件描述符指向了相同的文件表 前言 🎬 个人主页:ChenPi 🐻推荐专栏1: 《Linux C应用编程&#xf…

SystemVerilog学习(0)——目录与传送门

一、验证导论 SystemVerilog学习(1)——验证导论-CSDN博客文章浏览阅读403次。SystemVerilog自学,验证系统概述,什么是SVhttps://blog.csdn.net/apple_53311083/article/details/133953016 二、数据类型 SystemVerilog学习&…

hive自定义函数及案例

一.自定义函数 1.Hive自带了一些函数,比如:max/min等,但是数量有限,自己可以通过自定义UDF来方便的扩展。 2.当Hive提供的内置函数无法满足你的业务处理需要时,此时就可以考虑使用用户自定义函数。 3.根据用户自定义…

文献速递:多模态影像组学文献分享:多模态图注意力网络用于COVID-19预后预测

文献速递:多模态影像组学文献分享:多模态图注意力网络用于COVID-19预后预测 01 文献速递介绍 在处理像 COVID-19 这样的新出现的疾病时,患者和疾病特定因素(例如,体重或已知共病)对疾病的即时进展的影响…

python数据分析总结(pandas)

目录 前言 df导入数据 df基本增删改查 数据清洗 ​编辑 索引操作 数据统计 行列操作 ​编辑 df->types 数据格式化 ​编辑 日期数据处理 前言 此篇文章为个人python数据分析学习总结,总结内容大都为表格和结构图方式,仅供参考。 df导入数…

Python 从入门到精通 学习笔记 Day03

Python 从入门到精通 第三天 今日目标 流程控制语句、退出循环、练习学习的内容 一、流程控制语句 流程控制的三种方式:顺序语句、双分支语句、循环语句 双分支语句 Python 的双分支语句使用if-else语句实现。 其语法结构如下: if条件:#如果条作为真&#xff…

stm32 使用18B20 测试温度

用18b20 测试温度是非常常用的,不过18B20的调试不是这么容易的,有些内容网上很多的,不再重复说了,我先把波形说一下,再说程序部分: 整个都温度数据的顺序是: 1.700uS的低电平复位并测试18B20的…

使用MetaMask + Ganache搭建本地私有网络并实现合约部署与互动

我使用Remix编写合约,MetaMask钱包工具和Ganache搭建了一个私有网络,并且实现了合约的部署和互动。 在前面的博客中提到了 Remix在线环境及钱包申请 以及 Solidity的基本语法 ,没看过的小伙伴可以点击链接查看一下,都是在本专栏下…

Swift 如何实现自定义 Tab Bar

前言 每个 UI 设计师都喜欢美丽而有动画效果的 Tab Bar。然而,对于开发人员来说,实现这种设计可能是一场噩梦。当然,使用 Apple 的原生 Tab Bar 组件并专注于更有趣的事情,比如业务逻辑的实现,会更容易。但如果我们必…

CPU、MCU、MPU、DSP、FPGA各是什么?有什么区别?

1、CPU 中央处理器,简称 CPU(Central Processing Unit),中央处理器主要包括两个部分,即控制器、运算器,其中还包括高速缓冲存储器及实现它们之间联系的数据、控制的总线。 电子计算机三大核心部件就是CPU…

了解c++11中的新增

一,统一的初始化列表 在引入c11后,我们得出计划都可以用初始化列表进行初始化。 C11 扩大了用大括号括起的列表 ( 初始化列表 ) 的使用范围,使其可用于所有的内置类型和用户自 定义的类型, 使用初始化列表时,可添加等…

JS基础之原型原型链

JS基础之原型&原型链 原型&原型链构造函数创建对象prototypeprotoconstructor实例与原型原型的原型原型链其他constructorproto继承 原型&原型链 构造函数创建对象 我们先使用构造函数创建一个对象: function Person(){ } var person new Person();…

【数据结构和算法】到达首都的最少油耗

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 三、代码 四、复杂度分析 前言 这是力扣的2477题,难度为中等,解题方案有很多种&…

tomcat源码学习记录

tomcat 学习记录 tomcat 编译ant 下载编译运行 源码Debug运行 Bootstrap运行Tomcat查看状态 pom.xml测试EmbeddedTomcat 参考书籍博客 tomcat 编译 下载 tomcat 10 源码,解压然后idea导入 包存放的默认位置如下:base.path${user.home}/tomcat-build-lib…

Linux升级nginx版本

处于漏洞修复目的服务器所用nginx是1.16.0版本扫出来存在安全隐患,需要我们升级到1.17.7以上。 一般nginx默认在 /usr/local/ 目录,这里我的nginx是自定义的路径安装在 /app/weblogic/nginx 。 1.查看生产环境nginx版本 cd /app/weblogic/nginx/sbin/…

class065 A星、Floyd、Bellman-Ford与SPFA【算法】

class065 A星、Floyd、Bellman-Ford与SPFA【算法】 2023-12-9 19:27:02 算法讲解065【必备】A星、Floyd、Bellman-Ford与SPFA code1 A*算法模版 // A*算法模版(对数器验证) package class065;import java.util.PriorityQueue;// A*算法模版&#xff…

Elon Musk艾隆・马斯克的聊天机器人Grok上线可以使用啦,为X Premium Plus订阅者推出

艾隆・马斯克旗下的 AI 初创公司X(前身“推特”)开发的 ChatGPT 竞争对手 Grok 已经在 X 平台上正式推出。Grok 是一个基于生成模型 Grok-1的聊天机器人,它能够回答问题并提供最新的信息。与其他聊天机器人不同,Grok 可以实时获取…

luceda ipkiss教程 44:在PyCharm 中设置Template text

通过设置Template text,可以提升写代码的速度和版图设计效率。 设置了Template text,在PyCharm 命令窗口输入i3后按enter建,就可以快速输入 from ipkiss3 import all as i3 这一段代码,使用起来也是非常方便: 设置过程…

万界星空科技mes系统中看板管理

我们很多企业现在都有大屏,那到底万界星空科技低代码云mes系统管理中看板管理有什么作用?我总结了几条: 1.提高车间的生产效率 2.有效的监控设备运行状况 3.控制生产线运行 4.增加和改善用户体验 5.提高工作效率和工作安全性

课堂练习3.4:进程的切换

3-9 课堂练习3.4:进程的切换 进程切换是支持多进程的一个关键环节,涉及到 CPU 现场的保存和恢复,本实训分析 Linux 0.11 的进程切换过程。 第1关第一次进程切换过程分析 任务描述 本关任务回答问题: 在第一次进程切换时: 1.是从几号进程切换到几号进程?0 号进程和 1 号…