對自己完成的工程項目進行打包,因為是第一次嘗試,踩了各種各樣的坑。以防下次繼續踩坑,把相關問題以及解決辦法記錄下來。此次打包采用Python3.6,PyInstaller3.6,Windows64位系統。接下來就是整篇文章的精華了。
一、PyInstaller安裝
PyInstaller包的安裝可以在Anaconda環境下以conda install pyinstaller進行安裝,在PyCharm中可以通過pip install pyinstaller進行安裝。安裝成功后就可以着手進行打包了。當然打包需要用到以下一些相關命令了。
-h,--help | 查看該模塊的幫助信息 |
---|---|
-F,-onefile | 產生單個的可執行文件 |
-D,--onedir | 產生一個目錄(包含多個文件)作為可執行程序 |
-a,--ascii | 不包含 Unicode 字符集支持 |
-d,--debug | 產生 debug 版本的可執行文件 |
-w,--windowed,--noconsolc | 指定程序運行時不顯示命令行窗口(僅對 Windows 有效) |
-c,--nowindowed,--console | 指定使用命令行窗口運行程序(僅對 Windows 有效) |
-o DIR,--out=DIR | 指定 spec 文件的生成目錄。如果沒有指定,則默認使用當前目錄來生成 spec 文件 |
-p DIR,--path=DIR | 設置 Python 導入模塊的路徑(和設置 PYTHONPATH 環境變量的作用相似)。也可使用路徑分隔符(Windows 使用分號,Linux 使用冒號)來分隔多個路徑 |
-n NAME,--name=NAME | 指定項目(產生的 spec)名字。如果省略該選項,那么第一個腳本的主文件名將作為 spec 的名字 |
常用到的命令為-F、-D、-i、-p、-w等,其中-i用於指定生成項目的圖標,需要使用絕對路徑。對於打包結果較大的項目,選用-d生成目錄相比單可執行文件的打包方式,執行速度更快,但包含更加多的文件。本文的例子選中-D方式打包。
二、Python項目打包
打包分為單文件打包以及工程文件打包,單文件打包直接在命令窗口中采用pyinstaller -D filename.py,具體命令的添加可以參考表格中的命令。工程文件打包稍微有一些麻煩,主要是先生成主窗口的.spec文件,並修改相應的內容,最終執行.spec文件就可以逐步實現打包。以下是本次打包的工程目錄,主要文件均在moleculeSystem,還包括img文件夾,tempFile文件夾等眾多文件。
1.SPEC文件的生成
通過使用命令pyi-makespec -w xxx.py能夠生成相應的xxx.spec文件,具體如下:
# -*- mode: python ; coding: utf-8 -*- block_cipher = None a = Analysis(['mainWin.py'], pathex=['D:\\Python\\untitled1\\moleculeSystem'], binaries=[], datas=[], 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, [], exclude_binaries=True, name='mainWin', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, console=True ) coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, upx_exclude=[], name='mainWin')
spec文件中主要包含4個class: Analysis, PYZ, EXE和COLLECT.
- Analysis以py文件為輸入,它會分析py文件的依賴模塊,並生成相應的信息
- PYZ是一個.pyz的壓縮包,包含程序運行需要的所有依賴
- EXE根據上面兩項生成
- COLLECT生成其他部分的輸出文件夾,COLLECT也可以沒有
首先給出舉例項目的spec文件:
# -*- mode: python ; coding: utf-8 -*- block_cipher = None SETUP_DIR = 'D:\\Python\\untitled1\\' a = Analysis(['mainWin.py'], pathex=['D:\\Python\\untitled1\\moleculeSystem'], binaries=[], datas=[(SETUP_DIR+'img','img'),(SETUP_DIR+'temp_img','temp_img'),(SETUP_DIR+'mol\\totalFile','mol\\totalFile'),(SETUP_DIR+'tempFile','tempFile'),('D:\\Anaconda\\Anaconda3\\Lib\\site-packages\\vtkmodules','vtkmodules'),(SETUP_DIR+'AtomsInfo.txt','AtomsInfo.txt'),(SETUP_DIR+'BondInfos.txt','BondInfos.txt')], hiddenimports=['pkg_resources'], 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, [], exclude_binaries=True, name='mainWin', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, console=True ) coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, upx_exclude=[], name='mainWin')
1)py文件打包配置
針對多目錄多文件的python項目,打包時候需要將所有相關的py文件輸入到Analysis類里。Analysis類中的pathex定義了打包的主目錄,對於在此目錄下的py文件可以只寫文件名不寫路徑。如上的spec腳本,將所有項目中的py文件路徑以列表形式寫入Analysis,這里為了說明混合使用了絕對路徑和相對路徑。
2) 資源文件打包配置
資源文件包括打包的python項目使用的相關文件,如圖標文件,文本文件等。對於此類資源文件的打包需要設置Analysis的datas,如例子所示datas接收元組:datas=[(SETUP_DIR+'img','img')]。元組的組成為(原項目中資源文件路徑,打包后路徑),例子中的(SETUP_DIR+'img','img')表示從D:\Python\untitled1\下的img文件夾文件打包后放入打包結果路徑下的img目錄。
3)Hidden import配置
pyinstaller在進行打包時,會解析打包的python文件,自動尋找py源文件的依賴模塊。但是pyinstaller解析模塊時可能會遺漏某些模塊(not visible to the analysis phase),造成打包后執行程序時出現類似No Module named xxx。這時我們就需要在Analysis下hiddenimports中加入遺漏的模塊,如例子中所示。
4)遞歸深度設置
在打包導入某些模塊時,常會出現"RecursionError: maximum recursion depth exceeded"的錯誤,這可能是打包時出現了大量的遞歸超出了python預設的遞歸深度。因此需要在spec文件上添加遞歸深度的設置,設置一個足夠大的值來保證打包的進行,即
import sys sys.setrecursionlimit(5000)
5)去除不必要的模塊import
有時需要讓pyinstaller不打包某些用不到的模塊,可通過在excludes=[]中添加此模塊實現
3.使用spec文件打包
pyinstaller -D xxx.spec
打包生成兩個文件目錄build和dist,build為臨時文件目錄完成打包后可以刪除;dist中存放打包的結果,可執行文件和其它程序運行的關聯文件都在這個目錄下。
三、打包出現的問題
1.PyQt plugins缺失
使用PyQt編寫UI交互界面的python代碼在進行打包時可能會出現一些特別的問題。
執行使用了PyQt的打包程序,常會出現這樣的錯誤,提示缺少Qt platfrom plugin “windows”,如下圖
打包后程序運行后,使用png格式的圖標可以正常顯示,但使用的ico格式圖標不顯示。這兩個錯誤產生的問題都是因為打包時沒有將PyQt相關的動態鏈接庫目錄生成到打包目錄下,因此可以通過將這些需要的文件目錄拷貝到打包生成目錄下,解決plugin缺失問題。以使用PyQt5編寫的python軟件打包為例,完成打包后的結果目錄下包含PyQt5文件夾,將PyQt5\Qt\plugins下的所有內容(如下圖)拷貝到打包結果目錄。這樣就可以解決PyQt plugins缺失的問題。
2.動態鏈接庫缺失問題
一般的,打包后可能會缺失某些動態鏈接庫,造成執行程序出錯,如
ImportError: DLL load failed: 找不到指定的模塊
在打包過程中一般會有與此相關的warning提示(lib not found)無法找到這些動態鏈接庫。例如在32位版本的打包中,可能會出現scipy模塊相關的dll文件無法找到。這時就需要在打包的spec文件中指定動態鏈接庫路徑,使其關聯到打包后的路徑中。
binaries=[('路徑','.')]
3.Failed to execute script pyi_rth_pkgres
打包運行xxx.exe文件出現上述問題,是有可能因PyInstaller版本不是最新的造成的,可以通過GitHub網站https://github.com/pyinstaller/pyinstaller/archive/develop.zip下載,下載后解壓到某一文件夾中,采用命令行的方式進入解壓后的文件夾中,使用命令python setup.py install進行安裝。
4.Failed to execute script pyi_rth_certifi
出現此錯誤的原因是缺少了ssl證書,因此可以從Python官網上下載相應python版本的壓縮包,解壓后將解壓包中的_ssl.pyd(復制到Anaconda目錄下DLLs下覆蓋原文件),將
libcrypto-1_1.dll、libssl-1_1.dll(復制到Anaconda根目錄)即可解決問題。
5.no modules named xxxx
出現該問題是打包后的根目錄中沒有所需要的模塊,因此可以將對應安裝庫中的文件復制到根目錄中,此錯誤就不再出現。或者可以在hiddenimports中加入缺失的包。或者在datas中作同樣的處理。
6.Failed to execute script xxxx
出現該問題是在引用同級目錄中的文件時出現,在有python文件出增加__init__.py文件即可解決,該文件可什么都不寫。
7.路徑凍結
增加一個py文件,例如叫frozenPath.py
import sys import os def app_path(): """Returns the base application path.""" if hasattr(sys, 'frozen'): # Handles PyInstaller return os.path.dirname(sys.executable) return os.path.dirname(__file__)
其中的app_path()函數返回一個程序的執行路徑,為了方便我們將此文件放在項目文件的根目錄,通過這種方式建立了相對路徑的關系。源代碼中使用路徑時,以app_path()的返回值作為基准路徑,其它路徑都是其相對路徑。以本文中使用的python項目打包為例,如下所示:
import frozenPath # 根目錄路徑 appPath = frozenPath.app_path() background = QtGui.QPixmap(appPath+"/img/1.png")
這樣不論打包的文件放在哪台電腦上運行都不會出現路徑錯誤。
8.始終還有問題存在
可以采用以下命令解決
pyinstaller --hidden-import=pkg_resources -F xxxx.py
9.順便再記個PyQt5中QSplitter部件無法顯示其他窗口的背景
原因在於主窗口部件無法顯示背景,子窗口部件能夠顯示,基於此可以解決這種問題。
# 設置背景圖片 background = QtGui.QPixmap(appPath+"/img/1.png") palette1 = QtGui.QPalette() palette1.setBrush(self.backgroundRole(),QtGui.QBrush(background)) self.handle.setPalette(palette1) self.handle.setAutoFillBackground(True) # 設置背景透明 self.main_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
10.安裝PyQt5
使用豆瓣能夠告訴下載
下載PyQt5 pip install PyQt5 -i https://pypi.douban.com/simple 下載PyQt5-tools pip install PyQt5-tools -i https://pypi.douban.com/simple
希望這些能夠對還在打包程序路上掙扎的同志們有所幫助。