pyinstaller打包pyqt5,从入坑到填坑,详解

  • 2020 年 5 月 28 日
  • 筆記

  以上省略pyinstaller安装步骤,直入主题。先分享我的心路历程。

1.pyinstaller -F -i 1.ico UI_Main.py (先在CMD中 cd到 py文件对应的路径)

第一步打包成功,打开EXE,界面正常显示,但是连不上项目的IC板。故看到打包时的warning,怀疑是受此影响,几经辗转查阅,发现所缺的dll都是QT的dll,这里的QT指的是C++版的QT,于是我专程去官网下载安装了一个QT。

于是将所缺的dll如数转移到我的 UI_Main.py的同一根目录下:

2. 再次 pyinstaller -F -i 1.ico UI_Main.py 

此时 warning: lib not found 已无,打包完成,再次打开EXE,本人的UI还是无法连结到IC板,而在pycharm工程程序中却是完美运行。这验证说明这些Qt53xx文件是无害的。

3.查阅论坛,很多文章说到pyinstaller 打包时,导入路径的问题,我整理了些许方法,并进行了验证:

 

(1).主函数 import xx 导入模块,可免去其他形式导入【这里解决:pyinstaller打包后,打开报错:not find xx module】

(2).pyinstaller -F -i 1.ico UI_Main.py 后加 -p 路径/文件【主要解决:import 引发的路径查找不到的问题】

(3).UI_Main.spec文件中 pathex=[],binaries=[],datas=[] 分别添加需要额外添加的导入路径,额外添加的二进制文件,额外添加的数据文件。注意,spec文件搭配的CMD指令是 pyinstaller –clean -F UI_Main.spec -i 1.ico,–cean是清除pyinstaller 缓存文件,如何后接UI_Main.py则会使 spec文件被刷新

(4).CMD:   pyinstaller -F -i 1.ico UI_Main.py –add-datas=xxx.dll;.    【其实与(3)中添加datas=[]是一样的效果】

(5).主函数添加相应的路径 sys.path.append() 【其实也是和(1)差不多,能添加打包的搜索路径。】

 

以下是 spec文件的全内容,datas=[]中的”.”表示将该文件复制到根路径(UI_Main.py同),并从根路径搜索,添加到打包内容。

# -*- mode: python ; coding: utf-8 -*-

block_cipher = None


a = Analysis(['UI_Main.py'],
             pathex=['D:\\PyQt\\module\\AxpSys_DEBUG\\axp\\drivers', 'C:\\Python36\\Lib', 'D:\\PyQt'],
             binaries=[],
             datas=[('xxx1.dll', "."), ('xxx2.dll', "."), ('xxx3.csv', ".")],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          name='UI_Main',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=False , icon='1.ico')

  

 

4.我将这4种方法都试过,确实能解决相应问题,应该也能解决普遍问题。

  但仍未能解决我的UI不能连结IC板卡的问题。于是我思考一下,想到windows连结IC卡时,我是有安装过基于通讯的驱动,那怀疑坚定加肯定一定是,驱动部分的相关文件没有被打包进去。于是,我独立写了一个简单demo,对IC进行通讯的实验,并进行了每一个步骤的print侦测。惊奇的发现成功了!

  这里demo已经连上了IC板卡(也就是单片机啦啦啦),然后我葫芦画瓢,在UI_Main.py上造车,一样的轮子,竟然还是不行!

我努力对照spec 文件发现一模一样,也是根据 pyinstaller –clean -F UI_Main.spec -i 1.ico 相同的指令。我仔细反复查找,终于找到了两者唯一的不同!!!二者唯一不用的是 spec文件中 xxx1.dll 与 xxx2.dll的顺序是相反的【也就是一个1,2,3;另一个2,1,3】,我怀揣着忐忑试探着最后那一丝不似希望的希望。

  最后终于成功了!!!原理驱动装载的顺序也是影响最后IC读写的,我猜测是因为xxx2.dll 中有调用到 xxx1.dll的函数,但是因为我放的顺序是datas=[(‘xxx2.dll’, ‘.’), (‘xxx1.dll’,’.’), (‘xxx3.csv’,’.’)]  导致系统pyinstaller 先导入的xxx2.dll 因为引用不到 xxx1.dll中的函数,而在打包的时候直接报错,当我把顺序调整之后,才能完美运行!

 

  总结,关于程序需要额外加载的dll 文件【比如:上文提到的Qt53xx.dll此类与程序相关的,皆为内部导入,非额外】,额外具体指的是,在python 程序中使用到的WinDLL等,相关windows驱动时,可视为额外加载dll。

impor ctypes
ctypes.WinDLL('xxx1.dll')
ctypes.CDLL(path,'xxx2.dll')

  故,我这里因为是和单片机通讯,需要加载额外dll,所以会遇到这个问题,希望和我遇到类似问题与坑的同鞋们看到这篇文章与有此受益,感谢阅读,请不要怜惜自己给我点个赞吧。

  欢迎评论交流 pyinstaller 等的相关问题。

  补充一点:我的验证demo是在虚拟环境pipenv中验证的,其实pyinstaller网上的文章大部分是可以借鉴的,只是指令不同而使用时有所误导而已。