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網上的文章大部分是可以借鑒的,只是指令不同而使用時有所誤導而已。