目录
  • 针对pyqt应用的改进

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

    这个工具的名称就是pyinstaller,官方主页:

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

    准备

    工具准备

    第一件事情当然是安装这个库了,和往常一样,使用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。

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

    对了,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。