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

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.mfbz.cn/a/232603.html

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

相关文章

新版Android Studio 正则表达式匹配代码注释,删除注释,删除全部注释,IntelliJ IDEA 正则表达式匹配代码注释

正则表达式匹配代码注释 完整表达式拼接Android Studio 搜索匹配【IntelliJ IDEA 也是一样的】 完整表达式拼接 (/*{1,2}[\s\S]?*/)|(//[\x{4e00}-\x{9fa5}].)|(<!-[\s\S]?–>)|(^\s\n)|(System.out.println.*) 表达式拆解&#xff0c;可以根据自己需求自由组合&#x…

【Dubbo3云原生微服务开发实战】「Dubbo前奏导学」 RPC服务的底层原理和实现

RPC服务 RPC服务介绍RPC通信模式RPC架构组成RPC技术要点RPC通信技术选项分析RPC实战开发6大基础组件基础组件之Guava基础组件之Hutools基础组件之ReflectionASM基础组件之FastJSON/FastJSON2基础组件之FST相比FastJSON的优势 基础组件之Commons-Codec RPC框架层面选项分析RPC组…

Cocos Creator:创建棋盘

Cocos Creator&#xff1a;创建棋盘 创建地图三部曲&#xff1a;1. 创建layout组件2. 创建预制体Prefab&#xff0c;做好精灵贴图&#xff1a;3. 创建脚本LayoutSprite.ts收尾工作&#xff1a; 创建地图三部曲&#xff1a; 1. 创建layout组件 使用layout进行布局&#xff0c;…

sensitive word 敏感词(脏词) 如何忽略无意义的字符?达到更好的过滤效果?

忽略字符 说明 我们的敏感词一般都是比较连续的&#xff0c;比如 傻帽 那就有大聪明发现&#xff0c;可以在中间加一些字符&#xff0c;比如【傻!#$帽】跳过检测&#xff0c;但是骂人等攻击力不减。 那么&#xff0c;如何应对这些类似的场景呢&#xff1f; 我们可以指定特…

【论文精读】REACT: SYNERGIZING REASONING AND ACTING IN LANGUAGE MODELS

REACT: SYNERGIZING REASONING AND ACTING IN LANGUAGE MODELS 前言ABSTRACT1 INTRODUCTION2 REACT: SYNERGIZING REASONING ACTING3 KNOWLEDGE-INTENSIVE REASONING TASKS3.1 SETUP3.2 METHODS3.3 RESULTS AND OBSERVATIONS 4 DECISION MAKING TASKS5 RELATED WORK6 CONCLUSI…

Ubuntu20.04使用cephadm部署ceph集群

文章目录 Requirements环境安装Cephadm部署Ceph单机集群引导&#xff08;bootstrap&#xff09;建立新集群 管理OSD列出可用的OSD设备部署OSD删除OSD 管理主机列出主机信息添加主机到集群从集群中删除主机 部署Ceph集群 Cephadm通过在单个主机上创建一个Ceph单机集群&#xff0…

★102. 二叉树的层序遍历

102. 二叉树的层序遍历 很巧妙的&#xff0c;又学习了一种层次遍历的方法&#xff0c;就是说根据当前的队列的长度去遍历&#xff0c;遍历的当前队列的长度就是该层次的节点个数。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* Tr…

Flink 本地单机/Standalone集群/YARN模式集群搭建

准备工作 本文简述Flink在Linux中安装步骤&#xff0c;和示例程序的运行。需要安装JDK1.8及以上版本。 下载地址&#xff1a;下载Flink的二进制包 点进去后&#xff0c;选择如下链接&#xff1a; 解压flink-1.10.1-bin-scala_2.12.tgz&#xff0c;我这里解压到soft目录 [ro…

UniGui禁用缓存

今天有人问到如何禁用缓存&#xff0c;原因是引用了第三方js,css等文件&#xff0c;但是因为缓存的原因&#xff0c;修改后没有及时生效。 首先纠正一点&#xff0c;地址后加?不会禁用缓存 可以看到&#xff0c;后面即使加了&#xff1f;但仍然是from memory cache。对于浏览…

管理类联考——数学——真题篇——按知识分类——数据

文章目录 排列组合2023真题&#xff08;2023-05&#xff09;-数据分析-排列组合-组合-C运算-至少-需反面思考真题&#xff08;2023-08&#xff09;-数据分析-排列组合-相邻不相邻-捆绑法插空法-插空法注意空位比座位多1个&#xff0c;是用A&#xff1b;捆绑法内部排序用A&#…

ubuntu 20.04.6 server 服务器 下载与安装(配置静态IP)

下载地址&#xff1a;https://releases.ubuntu.com/20.04.6/ubuntu-20.04.6-live-server-amd64.iso 第一步&#xff1a; 准备U盘&#xff0c;使用软碟通将下载好的镜像写入到U盘中 软碟通网址&#xff1a;https://www.cn.ultraiso.net/xiazai.html 点击&#xff1a;文件 ->…

iOS——UIPickerView选择器

UIPickerView UIPickerView是 iOS 开发中常用的用户界面组件之一&#xff0c;用于在垂直方向上显示一个滚动的列表&#xff0c;用户可以通过滚动选择其中的一项。 UIPickerView的协议方法 UIPickerView和UItableView差不多&#xff0c;UIPickerView也要设置代理和数据源。UI…

JAVA+SSM+springboot+MYSQL企业物资库存进销存管理系统

。该系统从两个对象&#xff1a;由管理员和员工来对系统进行设计构建。主要功能包括首页、个人中心、员工管理、项目信息管理、仓库信息管理、供应商管理、项目计划管理、物资库存管理、到货登记管理、物资出库管理、物资入库管理等功能进行管理。本企业物资管理系统方便员工快…

Jenkins简单介绍

学习目标 知道jenkins应用场景能够安装部署jenkins服务器能够实现gitgithubjenkins手动构建能够实现gitgitlabjenkins自动发布系统 认识jenkins Jenkins是一个可扩展的持续集成引擎&#xff0c;是一个开源软件项目&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软…

【SpringBoot】请求参数

1. BS 架构 BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 在SpringBoot进行web程序开发时&#xff0c;它内置了一个核心的Servlet程序 DispatcherServlet&#xff0c;称之为 核…

ARP欺骗攻击

一.大概原理 ARP&#xff1a;address solution protocol 地址解析协议 ARP是一种基于局域网的TCP/IP协议&#xff0c;arp欺骗就是基于此协议的漏洞来达成我们的目的的&#xff0c;局域网中的数据传输并不是用ip地址传输的&#xff0c;而是靠mac地址。 我们如果出于某种目的想…

HTML中表格的语法及使用(详解)

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍HTML中表格的语法及详细使用以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习记录获&#xff0c;友友们有任何问题可以在评论区留…

学校安全检查系统

校园面积大、安全盲区多对学校安全管理带来诸多挑战&#xff1b;传统依靠人工纸质巡检记录存在漏检、管理难、联动差等诸多问题和缺点&#xff0c;巡检过程中很容易遗漏安全隐患的存续&#xff0c;从而导致安全事故的发生。 通过凡尔码平台模块化搭建学校安全管理系统&#xf…

黑豹程序员-EasyExcel实现导出

需求 将业务数据导出到excel中&#xff0c;老牌的可以选择POI&#xff0c;也有个新的选择EasyExcel。 有个小坑&#xff0c;客户要求样式比较美观&#xff0c;数字列要求千位符&#xff0c;保留2位小数。 可以用代码实现但非常繁琐&#xff0c;用模板就特别方便&#xff0c;模…

什么是RabbitMQ

目录 ​编辑 一、引言 二、概念和特性&#xff1a; 1、消息代理&#xff1a; 2、队列&#xff1a; 3、发布/订阅模型&#xff1a; 4、路由和交换机&#xff1a; 5、持久性&#xff1a; 6、灵活的消息模式&#xff1a; 7、可扩展性&#xff1a; 我的其他博客 一、引言…