目前在windows平臺上將Python程序打包成exe文件主要有三個工具。html
今天將一個Tkinter寫的界面程序打包成exe文件,三個工具都試了一遍,感受PyInstaller會比較好用一些。python
從這裏https://sourceforge.net/projects/py2exe/files/py2exe/0.6.9/ 選擇python版本和計算機位數對應的exe文件,雙擊便可安裝。windows
寫一個setup_py2exe.py文件app
from distutils.core import setup import py2exe options = {'py2exe': {'compressed': 1, 'optimize': 2, 'bundle_files': 1, }} setup(name='App', author='kinegratii', version='1.0.0', options=options, windows=[{"script": "app.py"}], zipfile=None )
執行python setup_py2exe.py py2exe便可,dist目錄就是最後生成的結果。ide
import py2exe函數
import py2exe
這個語句要保留,由於用PyCharm自動格式化的時候總會把這個語句優化掉。工具
UnicodeDecodeError異常優化
以前加了from __future__ import unicode_literals
這個語句,會報UnicodeDecodeError: 'utf8' codec can't decode byte 0xd1 in position 3: invalid continuation byte
ui
lxml庫spa
程序報的異常是ImportError: No module named lxml._elementpath
,但按照網上的說法加了includes參數能夠解決。
options={ 'py2exe': { 'includes': ['lxml.etree', 'lxml._elementpath', 'gzip'], } }
TypeError: expected string or buffer
這個異常是docx這個庫出現的。找了好久尚未什麼頭緒。
執行pip命令便可安裝
pip install cx_Freeze
setup_cx.py文件以下
from __future__ import unicode_literals import sys from cx_Freeze import setup, Executable base = None if sys.platform == "win32": base = "Win32GUI" includeFiles = [ (r"D:\py\tcl\tcl8.5", "tcl"), (r"D:\py\tcl\tk8.5", "tk") ] setup( name="App", version="1.0", description="A demo app", options={"build_exe": {"include_files": includeFiles, }}, executables=[Executable("app.py", base=base, includes=['lxml', 'lxml.tree', 'lxml._namepath'])] )
執行命令python setup_cx.py build,dist下的exe.開頭的文件夾(名字跟具體環境有關,好比個人是exe.win32-2.7)就是最後的生成的文件夾。
lxml
也須要明確包含lxml._elementpath
docx
也出現了和py2exe同樣的異常。
執行pip安裝
pip install pyinstaller ``` 安裝成功後在python的目錄下\Scripts文件夾多出pyinstaller.exe、pyinstaller.exe.manifest、pyinstaller-script.py等幾個文件。 ### 4.2 命令方式構建 把Scripts目錄加到系統的環境變量中,cd到腳本所在的目錄,而後執行下面的命令。 ``` pyinstaller app.py -F -w --clean ``` app.py 腳本文件 幾個選項含義 - -F 打包爲單一文件,和打包爲一個文件夾相對,默認爲後者 - -w 窗口程序,與控制檯相對 - --clean 每次清理中間產生的構建文件 生成的相關文件包括 - app.spec 配置文件 - build文件夾 構建中產生的中間文件 - dist/app文件夾 這裏的文件都是運行所須要的 ### 4.3 啓動腳本方式 命令行帶太多參數的話,每次都要輸入,比較麻煩,能夠通通寫在一個py腳本中。 PyInstaller也是一個標準的Python包,提供了`PyInstaller.main.run`這個方法。 4.2節中等效的python腳本以下 ``` if __name__ == '__main__': from PyInstaller.main import run params=[app.py', '-F', '-w', '--clean'] run(params) ``` 用Python解釋器執行這個腳本就能夠了。 ### 4.4 Q&A **lxml** 能夠解決lxml包含的問題,無需明確指定 **調試** 因爲用了沒有控制檯的-w方式,若是程序啓動有錯的話,只會彈出app return -1的對話框,沒有具體異常信息。能夠先去掉-w,用控制檯進行調試,全部的異常和程序中的print函數就顯示在控制檯上,方便調試。 **單exe資源文件路徑問題** 這個問題應該只要是最後打包成單個exe都會出現的問題。描述以下 最後打包的文件結構以下 ``` - XxxApp - app.exe - data - wpa.db ``` 程序中用下面語句引用wpa.db文件,會出現文件打不開的狀況 ``` BASE_DIR = os.path.abspath(os.path.dirname(__file__)) DB_FILE = os.path.join(BASE_DIR,'data', 'wpa.db') ``` 調試打印出`BASE_DIR`,發現路徑不是XxxApp,而是在用戶目錄下的某一個位置,相似以下 ``` c:\Users\kinegratii\AppData'Local\Temp\_MEI11~1\dadta\wpa.db ``` 這是由於**在單文件模式中運行程序的時候先將文件解壓到sys._MEIPASS指向的目錄下,因此引用資源文件就須要添加os.path.join(sys._MEIPASS,filename)**, 第一種方法,具體判斷程序當前模式。 ``` if getattr(sys, 'frozen', False): BASE_DIR = sys._MEIPASS else: BASE_DIR = os.path.dirname(__file__) ``` 第二種,就是將`__file__`改成sys.args[0],即 ``` BASE_DIR = os.path.abspath(os.path.dirname(sys.argv[0])) ``` 這樣打印的路徑就是正確的了,緣由在於`__file__`和`sys.args[0]`有點區別。 > `__file__` is the name of the current file, which may be different from the main script if you are inside a module or if you start a script using execfile() rather than by invoking python scriptname.py. `__file__` is generally your safer bet. 來自 http://stackoverflow.com/a/5851608 **icon圖標沒法顯示問題** 使用icon選項便可添加圖標,但有時候發現資源管理器的圖標能夠顯示,但運行程序後任務欄上的圖標卻沒法顯示。關於這個問題 。 > 在不一樣狀況下(好比資源管理器文件列表前面的圖標、桌面、開始菜單等)須要不同尺寸的圖標。若是尺寸不合適的話,可能出現有的地方顯示正確有的顯示不正確的狀況。最後幾個地方都要檢查一遍。 解決方案 > 應該準備四張不一樣尺寸(具體尺寸參見 http://stackoverflow.com/questions/3236115/which-icon-sizes-should-my-windows-applications-icon-include )的png文件 用png2icon腳本把它們合成一張icon圖標文件便可 ## 5 參考資料 - py2exe lxml error http://stackoverflow.com/a/5309733 - Creating an Executable from a Python Script | Matt Borgerson https://mborgerson.com/creating-an-executable-from-a-python-script - pyinstaller打包pyqt文件 - dcb3688 - 博客園 http://www.cnblogs.com/dcb3688/p/4211390.html - 使用pyinstaller打包python程序 - 魏哲的空間 https://blog.weizhe.net/?p=412