从零开始用 PyQt5 写一个 scihub 下载器(二)

2020-11-03 15:44:38 浏览数 (1)

在上一次的教程中,我们已经设计了程序界面,并生成了界面的 .py 脚本。在今天的教程中,我们将介绍如何使用这种界面与逻辑分离的 GUI 程序框架,构建主函数,并最终打包程序为可执行文件。

3. 构建主程序

3.1 界面与逻辑分离的 GUI 程序框架

这里我们采用单继承的界面封装方法,编写主程序 scihub_gui.py 框架,代码如下:

代码语言:javascript复制
## 单继承方法,能更好地进行界面与逻辑的分离import sysfrom PyQt5.QtWidgets import QApplication, QMainWindowfrom PyQt5.Qt import QStandardPathsfrom scihub_ui import Ui_MainWindowclass CustomUI(QMainWindow):    def __init__(self, parent=None):        super().__init__(parent) # 调用父类构造函数,self 就是一个 QMainWindow 对象        self.ui = Ui_MainWindow() # 创建UI 对象        self.ui.setupUi(self) # 构造UIif __name__ == '__main__':    app = QApplication(sys.argv) # 创建app,用 QApplication 类    cutomUI = CustomUI()    cutomUI.show()    sys.exit(app.exec_())

3.2 实现文献下载功能

本来自己写了个简单的脚本,但前两天在 GitHub 上看到了功能更完善的脚本,所以这里不妨做个调包侠。脚本地址:https://github.com/zaytoun/scihub.py

这个脚本可实现根据 DOI | PMID | URL 下载文献 PDF,使用方法也非常简单:

代码语言:javascript复制
from scihub import SciHubsh = SciHub()result = sh.download('http://ieeexplore.ieee.org/xpl/login.jsp?tp=&arnumber=1648853', path='paper.pdf')

我们将该 Scihub.py 脚本下载下来,放在 scihub_gui.py 的同一目录下,方便调用。

下载文献后,可用 PyPDF2 包提取 PDF 信息,以根据文献标题重命名 PDF 文件:

代码语言:javascript复制
from PyPDF2 import PdfFileReaderwith open('paper.pdf', 'rb') as f:    pdf = PdfFileReader(f)    info = pdf.getDocumentInfo()    title = info.title

3.3 PyQt5 的事件处理机制

PyQt5 有一个独一无二的信号和槽机制来处理事件。信号和槽用于对象之间的通信。当指定事件发生,一个事件信号会被发射。槽可以被任何 Python 脚本调用。当和槽连接的信号被发射时,槽会被调用。

在 Qt 中,每一个 QObject 对象和 PyQt 中所有继承自 QWidget 的控件(这些都是 QObject 的子对象)都支持信号与槽机制。当信号发射时,连接的槽函数将会自动执行。在 PyQt 5 中信号与槽通过 控件名.信号.connect(槽函数)方法连接。

在我们这个小程序中共包含了两类信号:

1.识别黏贴板的变化2.识别按钮动作

所以下一步我们为 scihub_gui.py 脚本加上亿点点细节,用 QApplication.clipboard() 读取黏贴板信息并重定向输出流(展示在文本区域内),绑定按钮事件(获取参数&触发请求),最后用 QMessageBox 弹出信息框。最终 scihub_gui.py 的代码如下:

代码语言:javascript复制
import sysfrom PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBoxfrom PyQt5.Qt import QStandardPathsfrom scihub_ui import Ui_MainWindowfrom scihub import SciHubfrom PyPDF2 import PdfFileReaderimport osclass CustomUI(QMainWindow):    def __init__(self, parent=None):        super().__init__(parent)        self.ui = Ui_MainWindow()        self.ui.setupUi(self)        self.cb = QApplication.clipboard()        self.cb.dataChanged.connect(self.monitor_clipboard)        self.ui.textEdit.setText(self.cb.text())        self.ui.pushButton.clicked.connect(self.buttClicked)    def monitor_clipboard(self):        if self.cb.text():            self.ui.textEdit.setText(self.cb.text())    def buttClicked(self):        try:            curPath = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation) # 默认保存在 ~/Documents            pdf_name = "/paper.pdf" # 默认文件名为 paper.pdf            test = SciHub()            textEditber = self.ui.textEdit.toPlainText()            test.download(textEditber, path= curPath   pdf_name)            with open(curPath   pdf_name, 'rb') as f:                pdf = PdfFileReader(f)                info = pdf.getDocumentInfo()                title = info.title                if title != None and title != "":                    file_name = title.replace(' ',"_")                    file_name = file_name   ".pdf"                    os.rename(curPath   pdf_name, curPath   "/"   file_name)                else:                    file_name = "paper.pdf"        except:            QMessageBox.information(self, '', '下载失败')        else:            QMessageBox.information(self, '', file_name   '下载完成')if __name__ == '__main__':    app = QApplication(sys.argv)    cutomUI = CustomUI()    cutomUI.show()    sys.exit(app.exec_())

•对于一些功能较复杂的程序我们可以把这些进行逻辑操作的函数放到一个新的 .py 文件中,这样可以方便后期维护代码。但在这里因为程序比较简单,所以就直接放在主程序中了。•踩过的坑:在 Qt 中, 当我们需要不调用 QFileDialog 保存文件时,得用 QStandardPaths 指定路径,否则打包出来的软件没法保存。

完成上面的步骤,直接运行程序就可以看到界面啦,复制 DOI 或 PMID,点击 Download即可下载文献了~

4. 打包程序为 Mac App

这里我们用 pyinstaller 来生成可执行程序,安装 pyinstaller

代码语言:javascript复制
pip install pyinstaller

准备一个软件图标 icon:

可用网站转换 png 至 ico 格式:https://www.easyicon.net/covert/

打包:

代码语言:javascript复制
pyinstaller --windowed --onefile --clean --noconfirm scihub_gui.py -p scihub.py -i scihub.icns

dist 目录下即可看到打包好的程序:

要是你也装了 Anaconda,你会发现这么简单的小程序体积居然有 100M ,这是因为 Anaconda 里内置了很多库,打包的时候打包了很多不必要的模块进去。所有为了缩小 App 的体积,我们最好在一个新的虚拟环境中进行打包。

创建环境:

代码语言:javascript复制
conda create -n scihubgui python=3.7

激活环境:

代码语言:javascript复制
conda activate scihubgui

安装依赖:

代码语言:javascript复制
pip install -r requirements.txt

requirements.txt

代码语言:javascript复制
altgraph==0.17beautifulsoup4==4.9.3certifi==2020.6.20chardet==3.0.4idna==2.10macholib==1.14powerline-status==2.7pyinstaller==4.0pyinstaller-hooks-contrib==2020.9PyPDF2==1.26.0PyQt5==5.15.1PyQt5-sip==12.8.1PySocks==1.7.1requests==2.24.0retrying==1.3.3six==1.15.0soupsieve==2.0.1urllib3==1.25.11

打包程序:

代码语言:javascript复制
pyinstaller --windowed --onefile --clean --noconfirm scihub_gui.py -p scihub.py -i scihub.icns

现在可以看到程序的大小缩小了一半以上(但感觉还是太大了,需要再研究下)。

我已将所有代码上传至 GitHub:https://github.com/zwbao/scihub_gui

教程推荐

1.Qt Documentation:https://doc.qt.io/2.Qt for Python:https://doc.qt.io/qtforpython/#project3.PyQt5 中文教程:https://github.com/maicss/PyQt5-Chinese-tutorial4.《Python Qt GUI与数据可视化编程》

0 人点赞