Python 创建一个能够筛选文件的PDF合并工具

ops/2025/3/1 8:56:16/

合并多个 PDF 文件。这款 PDF 合并工具允许用户浏览文件夹、选择 PDF 文件,并将其合并为一个新的 PDF 文件。我们将详细分析代码结构和如何一步步实现每个功能。
C:\pythoncode\new\PDFFileInFolderMergeToNewPDFFile.py

全部代码

python">import os
import wx
import PyPDF2class PDFMergerFrame(wx.Frame):def __init__(self, parent, title):super(PDFMergerFrame, self).__init__(parent, title=title, size=(900, 600))# Create main panelself.panel = wx.Panel(self)# Create toolbarself.toolbar = self.CreateToolBar()# Add toolbar buttonsbrowse_tool = self.toolbar.AddTool(wx.ID_ANY, "浏览", wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN), "浏览文件夹")merge_tool = self.toolbar.AddTool(wx.ID_ANY, "合成", wx.ArtProvider.GetBitmap(wx.ART_NEW), "合成PDF文件")self.toolbar.Realize()# Bind toolbar eventsself.Bind(wx.EVT_TOOL, self.on_browse, browse_tool)self.Bind(wx.EVT_TOOL, self.on_merge, merge_tool)# Create main sizermain_sizer = wx.BoxSizer(wx.HORIZONTAL)# Create left panel for foldersleft_panel = wx.Panel(self.panel)left_sizer = wx.BoxSizer(wx.VERTICAL)folder_label = wx.StaticText(left_panel, label="文件夹:")self.folder_listbox = wx.ListBox(left_panel, size=(200, -1), style=wx.LB_SINGLE)left_sizer.Add(folder_label, 0, wx.ALL, 5)left_sizer.Add(self.folder_listbox, 1, wx.EXPAND | wx.ALL, 5)left_panel.SetSizer(left_sizer)# Create middle panel for filesmiddle_panel = wx.Panel(self.panel)middle_sizer = wx.BoxSizer(wx.VERTICAL)file_label = wx.StaticText(middle_panel, label="子文件:")self.file_listbox = wx.ListBox(middle_panel, size=(200, -1), style=wx.LB_SINGLE)middle_sizer.Add(file_label, 0, wx.ALL, 5)middle_sizer.Add(self.file_listbox, 1, wx.EXPAND | wx.ALL, 5)middle_panel.SetSizer(middle_sizer)# Create button panelbutton_panel = wx.Panel(self.panel)button_sizer = wx.BoxSizer(wx.VERTICAL)add_button = wx.Button(button_panel, label=">")remove_button = wx.Button(button_panel, label="<")button_sizer.Add(add_button, 0, wx.ALL, 5)button_sizer.Add(remove_button, 0, wx.ALL, 5)button_panel.SetSizer(button_sizer)# Create right panel for selected filesright_panel = wx.Panel(self.panel)right_sizer = wx.BoxSizer(wx.VERTICAL)selected_label = wx.StaticText(right_panel, label="已选文件:")self.selected_listbox = wx.ListBox(right_panel, size=(200, -1), style=wx.LB_SINGLE)right_sizer.Add(selected_label, 0, wx.ALL, 5)right_sizer.Add(self.selected_listbox, 1, wx.EXPAND | wx.ALL, 5)right_panel.SetSizer(right_sizer)# Add panels to main sizermain_sizer.Add(left_panel, 1, wx.EXPAND | wx.ALL, 5)main_sizer.Add(middle_panel, 1, wx.EXPAND | wx.ALL, 5)main_sizer.Add(button_panel, 0, wx.EXPAND | wx.ALL, 5)main_sizer.Add(right_panel, 1, wx.EXPAND | wx.ALL, 5)# Set main sizerself.panel.SetSizerAndFit(main_sizer)#         # Set main sizer# self.panel.SetSizerAndFit(main_sizer)# Bind eventsself.folder_listbox.Bind(wx.EVT_LISTBOX, self.on_folder_select)add_button.Bind(wx.EVT_BUTTON, self.on_add_file)remove_button.Bind(wx.EVT_BUTTON, self.on_remove_file)# Initialize variablesself.root_path = ""self.folders_with_files = []# Center and show frameself.Centre()self.Show()def on_browse(self, event):"""Handle browse button click"""dlg = wx.DirDialog(self, "选择文件夹", style=wx.DD_DEFAULT_STYLE)if dlg.ShowModal() == wx.ID_OK:self.root_path = dlg.GetPath()self.scan_folders()dlg.Destroy()def scan_folders(self):"""Scan folders and list those with PDF files"""if not self.root_path:returnself.folders_with_files = []self.folder_listbox.Clear()for root, dirs, files in os.walk(self.root_path):has_pdf = Falsefor file in files:if file.lower().endswith('.pdf'):has_pdf = Truebreakif has_pdf:self.folders_with_files.append(root)folder_name = os.path.basename(root) or rootself.folder_listbox.Append(folder_name)def on_folder_select(self, event):"""Handle folder selection"""selected_index = self.folder_listbox.GetSelection()if selected_index == wx.NOT_FOUND:returnselected_folder = self.folders_with_files[selected_index]self.file_listbox.Clear()for file in os.listdir(selected_folder):if file.lower().endswith('.pdf'):self.file_listbox.Append(file)def on_add_file(self, event):"""Add selected file to the list of files to merge"""selected_file_index = self.file_listbox.GetSelection()selected_folder_index = self.folder_listbox.GetSelection()if selected_file_index == wx.NOT_FOUND or selected_folder_index == wx.NOT_FOUND:returnselected_file = self.file_listbox.GetString(selected_file_index)selected_folder = self.folders_with_files[selected_folder_index]full_path = os.path.join(selected_folder, selected_file)# Check if already in the listfor i in range(self.selected_listbox.GetCount()):if self.selected_listbox.GetClientData(i) == full_path:returnself.selected_listbox.Append(selected_file, full_path)def on_remove_file(self, event):"""Remove selected file from the list of files to merge"""selected_index = self.selected_listbox.GetSelection()if selected_index != wx.NOT_FOUND:self.selected_listbox.Delete(selected_index)def on_merge(self, event):"""Merge selected PDF files"""if self.selected_listbox.GetCount() == 0:wx.MessageBox("请选择至少一个PDF文件", "警告", wx.OK | wx.ICON_WARNING)return# Ask for output filewith wx.FileDialog(self, "保存合并的PDF", wildcard="PDF files (*.pdf)|*.pdf",style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog:if fileDialog.ShowModal() == wx.ID_CANCEL:returnoutput_path = fileDialog.GetPath()try:merger = PyPDF2.PdfMerger()# Add all selected filesfor i in range(self.selected_listbox.GetCount()):file_path = self.selected_listbox.GetClientData(i)merger.append(file_path)# Write to output filemerger.write(output_path)merger.close()wx.MessageBox(f"PDF文件已成功合并为: {output_path}", "成功", wx.OK | wx.ICON_INFORMATION)except Exception as e:wx.MessageBox(f"合并PDF时出错: {str(e)}", "错误", wx.OK | wx.ICON_ERROR)class PDFMergerApp(wx.App):def OnInit(self):frame = PDFMergerFrame(None, "PDF合并工具")self.SetTopWindow(frame)return Trueif __name__ == "__main__":app = PDFMergerApp()app.MainLoop()
背景

wxPython 是一个基于 wxWidgets 的 Python GUI 库,它提供了创建跨平台桌面应用程序的强大工具。PyPDF2 则是一个 Python 库,用于处理 PDF 文件的操作,如合并、拆分、提取文本等。

本示例程序结合了 wxPython 用于构建图形界面和 PyPDF2 用于处理 PDF 合并的功能。

主要功能
  1. 浏览文件夹:用户可以选择一个文件夹,程序会扫描该文件夹下所有的 PDF 文件。
  2. 选择文件:用户可以从文件夹中选择需要合并的 PDF 文件。
  3. 合并文件:用户选择多个 PDF 文件后,程序会将其合并成一个新的 PDF 文件。

接下来,让我们详细分析代码。

代码解析

1. 初始化 wx.Frame 窗口
python">class PDFMergerFrame(wx.Frame):def __init__(self, parent, title):super(PDFMergerFrame, self).__init__(parent, title=title, size=(900, 600))

我们创建了一个继承自 wx.Frame 的类 PDFMergerFrame,这是应用程序的主窗口。窗口的大小为 900x600

2. 创建工具栏
python"># Create toolbar
self.toolbar = self.CreateToolBar()# Add toolbar buttons
browse_tool = self.toolbar.AddTool(wx.ID_ANY, "浏览", wx.ArtProvider.GetBitmap(wx.ART_FOLDER_OPEN), "浏览文件夹")
merge_tool = self.toolbar.AddTool(wx.ID_ANY, "合成", wx.ArtProvider.GetBitmap(wx.ART_NEW), "合成PDF文件")
self.toolbar.Realize()

在窗口中添加了一个工具栏,并且为工具栏添加了两个按钮:

  • 浏览:用来选择文件夹,浏览文件夹并列出包含 PDF 文件的文件夹。
  • 合成:用来合并用户选择的 PDF 文件。

按钮的图标通过 wx.ArtProvider.GetBitmap 方法加载。

3. 创建布局和界面控件

我们使用 wx.BoxSizer 创建了主界面布局,并将不同的控件添加到面板中。

python"># Create main sizer
main_sizer = wx.BoxSizer(wx.HORIZONTAL)

main_sizer 是一个水平的 BoxSizer,用于将控件横向排列。接下来,我们定义了四个面板,分别用于显示:

  • 文件夹选择:显示文件夹列表。
  • 文件选择:显示当前文件夹中的 PDF 文件。
  • 按钮面板:显示添加和移除文件的按钮。
  • 已选文件:显示用户选择的文件列表。

每个面板使用 wx.Panel 创建,并且通过 wx.ListBox 控件展示文件夹或文件列表。

4. 文件夹扫描功能
python">def scan_folders(self):"""Scan folders and list those with PDF files"""if not self.root_path:returnself.folders_with_files = []self.folder_listbox.Clear()for root, dirs, files in os.walk(self.root_path):has_pdf = Falsefor file in files:if file.lower().endswith('.pdf'):has_pdf = Truebreakif has_pdf:self.folders_with_files.append(root)folder_name = os.path.basename(root) or rootself.folder_listbox.Append(folder_name)

scan_folders 函数扫描用户选择的文件夹并列出其中包含 PDF 文件的子文件夹。使用 os.walk 递归遍历文件夹,并筛选出后缀为 .pdf 的文件。如果文件夹中包含 PDF 文件,则将文件夹添加到 folder_listbox

5. 选择文件夹后的操作
python">def on_folder_select(self, event):"""Handle folder selection"""selected_index = self.folder_listbox.GetSelection()if selected_index == wx.NOT_FOUND:returnselected_folder = self.folders_with_files[selected_index]self.file_listbox.Clear()for file in os.listdir(selected_folder):if file.lower().endswith('.pdf'):self.file_listbox.Append(file)

用户在 folder_listbox 中选择一个文件夹后,on_folder_select 方法会被触发。此时,程序会列出该文件夹中的所有 PDF 文件,并显示在 file_listbox 中供用户选择。

6. 添加和移除文件
python">def on_add_file(self, event):"""Add selected file to the list of files to merge"""selected_file_index = self.file_listbox.GetSelection()selected_folder_index = self.folder_listbox.GetSelection()if selected_file_index == wx.NOT_FOUND or selected_folder_index == wx.NOT_FOUND:returnselected_file = self.file_listbox.GetString(selected_file_index)selected_folder = self.folders_with_files[selected_folder_index]full_path = os.path.join(selected_folder, selected_file)# Check if already in the listfor i in range(self.selected_listbox.GetCount()):if self.selected_listbox.GetClientData(i) == full_path:returnself.selected_listbox.Append(selected_file, full_path)

当用户选择文件并点击 > 按钮时,选中的文件将被添加到 selected_listbox 中。在添加之前,程序会检查该文件是否已经在列表中,防止重复添加。

移除文件的操作也是类似的,用户可以通过 < 按钮从 selected_listbox 中删除已选文件。

7. 合并PDF文件
python">def on_merge(self, event):"""Merge selected PDF files"""if self.selected_listbox.GetCount() == 0:wx.MessageBox("请选择至少一个PDF文件", "警告", wx.OK | wx.ICON_WARNING)return# Ask for output filewith wx.FileDialog(self, "保存合并的PDF", wildcard="PDF files (*.pdf)|*.pdf",style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog:if fileDialog.ShowModal() == wx.ID_CANCEL:returnoutput_path = fileDialog.GetPath()try:merger = PyPDF2.PdfMerger()# Add all selected filesfor i in range(self.selected_listbox.GetCount()):file_path = self.selected_listbox.GetClientData(i)merger.append(file_path)# Write to output filemerger.write(output_path)merger.close()wx.MessageBox(f"PDF文件已成功合并为: {output_path}", "成功", wx.OK | wx.ICON_INFORMATION)except Exception as e:wx.MessageBox(f"合并PDF时出错: {str(e)}", "错误", wx.OK | wx.ICON_ERROR)

最后,合并 PDF 的操作由 on_merge 方法实现。用户选择了要合并的 PDF 文件后,程序会弹出文件保存对话框,询问用户保存合并后文件的位置。使用 PyPDF2.PdfMerger 将所有选择的 PDF 文件合并并保存。

运行结果

在这里插入图片描述


http://www.ppmy.cn/ops/162187.html

相关文章

python量化交易——金融数据管理最佳实践——qteasy创建本地数据源

文章目录 qteasy金融历史数据管理总体介绍本地数据源——DataSource对象默认数据源查看数据表查看数据源的整体信息最重要的数据表其他的数据表 从数据表中获取数据向数据表中添加数据删除数据表 —— 请尽量小心&#xff0c;删除后无法恢复&#xff01;&#xff01;总结 qteas…

Java实现斗地主-做牌以及对牌排序

卡牌类 public class Card {private String size;//大小private String color;//花色private int value;//权值public Card() {}public Card(String size, String color, int value) {this.size size;this.color color;this.value value;}public String toString(){return …

51单片机-串口通信编程

串行口工作之前&#xff0c;应对其进行初始化&#xff0c;主要是设置产生波特率的定时器1、串行口控制盒中断控制。具体步骤如下&#xff1a; 确定T1的工作方式&#xff08;编程TMOD寄存器&#xff09;计算T1的初值&#xff0c;装载TH1\TL1启动T1&#xff08;编程TCON中的TR1位…

【FL0087】基于SSM和微信小程序的民宿短租系统

&#x1f9d1;‍&#x1f4bb;博主介绍&#x1f9d1;‍&#x1f4bb; 全网粉丝10W,CSDN全栈领域优质创作者&#xff0c;博客之星、掘金/知乎/b站/华为云/阿里云等平台优质作者、专注于Java、小程序/APP、python、大数据等技术领域和毕业项目实战&#xff0c;以及程序定制化开发…

北京大学DeepSeek提示词工程与落地场景(PDF无套路免费下载)

近年来&#xff0c;大模型技术飞速发展&#xff0c;但许多用户发现&#xff1a;即使使用同一款 AI 工具&#xff0c;效果也可能天差地别——有人能用 AI 快速生成精准方案&#xff0c;有人却只能得到笼统回答。这背后的关键差异&#xff0c;在于提示词工程的应用能力。 北京大…

吃一堑长一智

工作中经历&#xff0c;有感触记录下 故事一 以前在一家公司时&#xff0c;自己是一名开发人员&#xff0c;遇到问题请教领导解决方案&#xff0c;当时领导给了建议&#xff0c;后来上线后出问题了&#xff0c;背了锅。心里想的是领导说这样做的呀&#xff0c;为什么出问题还…

Python测试框架Pytest的参数化

上篇博文介绍过&#xff0c;Pytest是目前比较成熟功能齐全的测试框架&#xff0c;使用率肯定也不断攀升。 在实际工作中&#xff0c;许多测试用例都是类似的重复&#xff0c;一个个写最后代码会显得很冗余。这里&#xff0c;我们来了解一下pytest.mark.parametrize装饰器&…

多模块 Maven 项目的管理(一)

为什么选择多模块 Maven 项目 在 Java 项目开发的版图中&#xff0c;Maven 早已成为构建和依赖管理的中流砥柱 。而随着项目规模的不断膨胀&#xff0c;功能的日益复杂&#xff0c;单模块 Maven 项目逐渐显得力不从心&#xff0c;多模块 Maven 项目应运而生&#xff0c;成为大…