使用pyinstaller打包python PyQt5程序

当你有打包你的python应用的需求以后,你可能会像搜索引擎寻求帮助,你或许曾经搜索过“python 转 exe”,“python打包”等等这样的字眼,你或许曾看到过各种各样的相关解决方案,本文介绍的是其中的一种,但是可以负责任的说,这是目前最好的方案,并且是跨平台最好的方案,也就是说使用这个叫pyinstaller的工具,你可以把同样的代码打包在Windows,Linux以及macOs上运行。

这个工具的名称就是pyinstaller,官方主页:
http://www.pyinstaller.org/

这个工具将打包工作封装成一条简单的指令就能完成的动作。

准备

工具准备

第一件事情当然是安装这个库了,和往常一样,使用pip

pip install pyinstaller

源代码准备

因为我们是针对一个GUI应用来打包,那么我们需要简单的改造一下源代码,如果你是在打包命令行,则不需要进行这个操作。

首先,我们要创建一个入口文件,名称为 wifidrop.pyw,名字无所谓,它将成为你最终的软件名称,这里需要注意的是,我们的文件后缀为pyw,比py文件后缀多了一个w,而w的意思是window,也就是说告诉python环境,我们这个软件是一个窗口应用,如果不这么做,在运行软件的时候,除了有我们的GUI界面以外,python还会留 一个命令行窗口在后边,这显然不是我们想要的,而使用了pyw后缀以后,python则不会显示命令行窗口。

在 wifidrop.pyw 不需要做什么太多东西,仅仅是简单的调用main.py中的main函数就可以了。

import sys
from main import main

if __name__ == '__main__':
    excode = main()
    sys.exit(excode)

打包

做好代码相关的准备以后,就可以使用pyinstaller来打包软件了,过程也非常简单,一行命令就能解决,在命令行中运行

pyinstaller --clean -F -w wifidrop.pyw -i Treetog-I-Documents.ico

参数说明

–clean :告诉pyinstaller删除缓存和临时文件
-F :告诉pyinstaller将打包的结果放在一个exe文件中,也就是说最终结果将只有一个exe文件,如果不使用这个参数,那么结果会是一个exe加很多依赖文件,不利于我们分发软件。
-w :告诉pyinstaller我们要生成的是一个窗口应用
-i :为我们的应用指定一个图标,否则默认的话会使用python图标
一般来说,这些参数就足够日常使用了,如果需要更深入的功能,比如加密等等,就需要阅读手册了。

这条命令成功运行完以后,你将会在项目目录中看到build和dist两个文件夹,最终的可执行文件就放置在dist文件家中。

针对PyQt应用的改进

目前的小问题

当我们尝试运行在dist中生成的wifidrop.exe文件的时候,你将发现,程序无法运行,不知道你还有没有印象,至今为止,我们都一直在main.py中使用loadUi函数加载Qt Creator创建的ui的方式来创建GUI界面,那么仔细观察一下dist文件夹中,里边并没有ui文件,对吧?

因为pyinstaller仅仅是一个py应用的打包工具,它并不知道其他的文件的存在,所以,你可以尝试把我们的两个ui文件拷贝到dist文件夹中之后再运行这个exe。

使用pyinstaller打包python PyQt5程序_第1张图片

你会发现现在应用可以正常使用了。但是这样会不会有什么风险?

对了,ui文件说白了是一个xml格式的文本文件,如果使用我们软件的人有意或者无意的修改了这两个文件中的内容,我们的软件极有可能就没办法运行了,因此,在分发我们的软件的时候,要避免使用ui文件。

ui文件的好处是在开发的时候,可以迅速的通过Qt Creator修改UI。

解决办法

当然PyQt也提供了很好的解决办法,它提供了一个小脚本,可以帮我们把ui文件转成py文件,并将UI转换成类。我们通过运行下边这两条命令,将mainwindow和dialog转成python类。

python -m PyQt5.uic.pyuic -x dialog.ui -o dialog.py
python -m PyQt5.uic.pyuic -x mainwindow.ui -o mainwindow.py

每一个ui文件会对应一个py文件,而py文件中会有相应的类。

有了py模块以后,我们还需要在实例化ui的地方(也就是main.py)中将加载ui文件的方式改为实例化python类的方式来加载ui,分别要修改MainWindow和SendDialog的初始化函数,选择2 option的方式来加载ui

class MainWindow(QMainWindow):
    """Main window"""
    def __init__(self):
        super(MainWindow, self).__init__()

        # UI setup - 1 option
        # dynamic load ui for development purpose
        # self.ui = loadUi('./mainwindow.ui', self)

        # Use py to setup UI - 2 option
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.setStatusBar(None)  # https://doc.qt.io/qt-5/qmainwindow.html#setStatusBa
class SendDialog(QDialog):
    def __init__(self, url_list, socket_server_thread, device_discover_thread, socket_broadcast):
        super(SendDialog, self).__init__()

        # UI setup - 1 option
        # dynamic load ui for development purpose
        # self.ui = loadUi('./dialog.ui', self)

        # Use py to setup UI - 2 option
        self.ui = Ui_dialog()
        self.ui.setupUi(self)

再次打包

代码修正之后,我们来使用上边相同的pyinstaller命令打包,成功运行后,点击生成的wifidrop.exe,你会发现在无需ui文件的情况下,软件也能很好的运行了。

代码仓库说明

本文中用到的打包命令和ui转py命令,可以在github 仓库 https://github.com/pythonlibrary/wifidrop 中的 tutorial-6-direct-dist 和 tutorial-6-dist tag下找到,这两个tag的区别是:前一个使用了加载ui文件的方式显示UI,后边一个修正了这个问题,使用类实例化的方式显示UI。

更多关于使用pyinstaller打包python程序的文章请查看下面的相关文章

你可能感兴趣的