pyqt5常用函数pdf(PyQt5从零开始制作PDF)(1)

此前,我已经写了三篇关于 Ui 界面的文章,分别是:

使用 Pyqt5 制作猜数游戏 GUI、[Python实战]你也能写的计时器程序和[Python实战]Python制作天气查询软件。这次,我们使用 Python 实现 PDF 阅读器。

这篇文章,主要介绍如何实现主界面,以及添加、删除图书封面,后续会不断完善程序功能。

效果图

pyqt5常用函数pdf(PyQt5从零开始制作PDF)(2)

UI 设计

首先使用 Qt Designer 设计出图形界面:

新建一个 MainWindow 主界面,然后设置一个 toolbar,并在 toolbar 中添加三个 action,并为每个 action 设置好相应图标。

也可以直接 compile 我制作好的 PyReader.ui 文件,或者导入 Ui_PyReader.py 文件。

pyqt5常用函数pdf(PyQt5从零开始制作PDF)(3)

依赖要求

  1. Python3
  2. PyQt5
  3. PyMuPDF

主要任务

我们使用 PyMuPDF 来解析 PDF ,来获取 PDF 文本信息。

我们只要在 cmd 中输入:pip install PyMuPDF,即可安装 PyMuPDF。

# 导入 PyMuPDF import fitz

在本节中,我们只需了解以下几个基本操作:

fitz.open() 函数用来读取 PDF 文件内容,doc.loadPage() 函数用来获取具体某一页的信息。特别的 ,我们使用loadPage(0) 来获取封面信息。

# 读取 PDF doc = fitz.open(fname) # 获取第 n 页内容 page = doc.loadPage(n)

本节主要的内容就是把封面渲染到主界面中,并完成添加与删除封面的任务。

显示表格

我们采用 QtWidgets.QTableWidget 表格控件来显示封面。

首先让我们设置表格样式与功能:

其中,我们设置了单元格的纵横比为 4 : 3,以及其他的一些静态属性,并将 self.table 与右键菜单绑定,支持点击单元格调用 self.generateMenu 函数。

def _setTableStyle(self): # 开启水平与垂直滚轴 self.table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) # 设置 5 行 8 列 的表格 self.table.setColumnCount(8) self.table.setRowCount(5) # 设置标准宽度 self.width = self.screen.width() // 8 # 设置单元格的宽度 for i in range(8): self.table.setColumnWidth(i, self.width) # 设置单元格的高度 # 设置纵横比为 4 : 3 for i in range(5): self.table.setRowHeight(i, self.width * 4 // 3) # 隐藏标题栏 self.table.verticalHeader().setVisible(False) self.table.horizontalHeader().setVisible(False) # 禁止编辑 self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) # 不显示网格线 self.table.setShowGrid(False) # 将单元格绑定右键菜单 # 点击单元格,调用 self.generateMenu 函数 self.table.setContextMenuPolicy(Qt.CustomContextMenu) self.table.customContextMenuRequested.connect(self.generateMenu)

添加封面

首先让我们来看如何生成 TableWidget 可显示的 图像类文件。

我们通过 doc.loadPage(0) 获取页面对象,并传递给 render_pdf_page() 函数,设置缩放比为 1 : 1。首先构建 QImage 对象,在通过 convertFromImage 函数将 QImage 对象转化为可显示对象。

# 显示 PDF 封面 # page_data 为 page 对象 def render_pdf_page(page_data, for_cover=False): # 图像缩放比例 zoom_Matrix = fitz.Matrix(4, 4) if for_cover: zoom_matrix = fitz.Matrix(1, 1) # 获取封面对应的 Pixmap 对象 # alpha 设置背景为白色 pagePixmap = page_data.getPixmap( matrix = zoom_matrix, alpha=False) # 获取 image 格式 imageFormat = QtGui.QImage.Format_RGB888 # 生成 QImage 对象 pageQImage = QtGui.QImage( pagePixmap.samples, pagePixmap.width, pagePixmap.height, pagePixmap.stride, imageFormat) # 生成 pixmap 对象 pixmap = QtGui.QPixmap() pixmap.convertFromImage(pageQImage) return pixmap

接着,我们就要想单元格中添加封面图片:

我们使用工具栏中的 号来添加 PDF 封面。

self.addbar.triggered.connect(self.open),当点击 时,就会调用 self.open 函数。

我们通过 getOpenFileName() 函数来获取文件地址,self 后面的三个参数分别是窗口名称,文件默认路径以及支持的文件类型。这个函数返回文件的地址。

filter_book() 函数用来确保不会重复显示同一本书的封面。

def getfile(self): # 打开单个文件 fname, _ = QFileDialog.getOpenFileName(self, 'Open files', './', '(*.pdf)') return fname def open(self): # 打开文件 fname = self.getfile() if self.filter_book(fname): self.setIcon(fname) # 获取无重复图书的地址 def filter_book(self, fname): if not fname: return False if fname not in self.booklist: self.booklist.append(fname) return True return False

然后,我们就要将 PDF 封面渲染到主界面上:

label.setScaledContents(True) 使得图片可以充满 label。self.table.setCellWidget(self.x, self.y, label) 用来设置标签的行与列。最后确保每八个元素换行,换行后将列数清零。

def setIcon(self, fname): # 打开 PDF doc = fitz.open(fname) # 加载封面 page = doc.loadPage(0) # 生成封面图像 cover = render_pdf_page(page, True) label = QLabel(self) # 设置图片自动填充 label label.setScaledContents(True) # 设置封面图片 label.setPixmap(QPixmap(cover)) # 设置单元格元素为 label self.table.setCellWidget(self.x, self.y, label) # 删除 label 对象,防止后期无法即时刷新界面 # 因为 label 的生存周期未结束 del label # 设置当前行数与列数 self.crow, self.ccol = self.x, self.y # 每 8 个元素换行 if (not self.y % 7) and (self.y): self.x = 1 self.y = 0 else: self.y = 1

右键菜单

上面我们已经提到,如何将单元格与右键菜单绑定。

pyqt5常用函数pdf(PyQt5从零开始制作PDF)(4)

本次教程中,右键菜单只有两项,分别为开始阅读(暂未实现),以及删除图书。

def generateMenu(self, pos): row_num = col_num = -1 # 获取选中的单元格的行数以及列数 for i in self.table.selectionModel().selection().indexes(): row_num = i.row() col_num = i.column() # 若选取的单元格中有元素,则支持右键菜单 if (row_num < self.crow) or (row_num == self.crow and col_num <= self.ccol): menu = QMenu() # 添加选项 item1 = menu.addAction('开始阅读') item2 = menu.addAction('删除图书') # 获取选项 action = menu.exec_(self.table.mapToGlobal(pos)) if action == item1: pass # 点击选项二,调用 self.delete_book 删除图书 elif action == item2: self.delete_book(row_num, col_num)

接下来,让我们看如何删除图书:

首先维护一个 self.booklist ,里面储存无重复 PDF 文件地址。首先获取图书在 booklist 中的索引,在 booklist 中删除该元素。接着清空选中单元格之后(包含选中单元格)的所有单元格的内容。最后将 booklist 中 index 之后的图书地址重新显示到 table 上。简单地说,就是删除选中单元格,并将之后单元格向前挪一位。

# 删除图书 def delete_book(self, row, col): # 获取图书在列表中的位置 index = row * 8 col self.x = row self.y = col if index >= 0: self.booklist.pop(index) i, j = row, col while 1: # 移除 i 行 j 列单元格的元素 self.table.removeCellWidget(i, j) # 一直删到最后一个有元素的单元格 if i == self.crow and j == self.ccol: break if (not j % 7) and j: i = 1 j = 0 else: j = 1 # 如果 booklist 为空,设置当前单元格为 -1 if not self.booklist: self.crow = -1 self.ccol = -1 # 删除图书后,重新按顺序显示封面图片 for fname in self.booklist[index:]: self.setIcon(fname)

pyqt5常用函数pdf(PyQt5从零开始制作PDF)(5)

点击下面链接,获取源码。

,