pyinstaller

簡述

pyinstaller 打包的流程:讀取編寫好的 python 腳本,分析其中調用的模塊和庫,而後收集這些文件的副本(包括 Python 的解釋器)。最後把副本與腳本,可執行文件等放在一個文件夾中,或者可選地封裝在一個可執行文件中。html

基本使用方法

安裝 pyinstaller

pip install pyinstaller
複製代碼

生成 spec 文件

進入主程序目錄,輸入 pyi-makespec -w main.py 生成 main.spec 文件。python

幾個經常使用參數git

參數 說明
-F,-onefile 打包一個單個文件
-D,-onedir 打包多個文件,在 dist 中生成不少依賴文件
-w,-windowed,-noconcole 當程序啓動的時候不會打開命令行(只對windows有效)

根據須要編輯 spec 文件

onefile 模式web

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

block_cipher = None


a = Analysis(['main.py'],
             pathex=['C:\\Users\\lunckl\\Desktop\\qcm-python'],
             binaries=[('./NovaQCM.exe', '.')],  # non-python modules needed by the scripts
             datas=[('./*.ico', '.'), ('./*.png', '.'), ('./QCM/*.txt', 'QCM')],  # non-binary files included in the app
             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='qcm',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=False,
          icon='favicon.ico')

複製代碼

onedir 模式shell

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

block_cipher = None


a = Analysis(['main.py'],
             pathex=['C:\\Users\\lunckl\\Desktop\\qcm-python'],
             binaries=[('./NovaQCM.exe', '.')],
             datas=[('./*.ico', '.'), ('./*.png', '.'), ('./QCM/*.txt', 'QCM')],
             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='qcm',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=False,
          icon='favicon.ico')
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               upx_exclude=[],
               name='QCM')

複製代碼

執行打包命令

pyinstaller main.spec
複製代碼

進行測試

進入生成的 dist 目錄,執行 main.exewindows

常見問題

打包多進程、線程

用 pyinstaller 打包好 exe 後,雙擊運行,會出現無限循環地進入主程序的狀況。此時須要在調用多進程的前面加上以下的代碼:bash

if __name__ == "__main__":
    multiprocessing.freeze_support()  # 不加這句,打包的程序就進不了下面的子進程了
    p1 = multiprocessing.Process(target=callback, target=(, ))
    p1.start()
複製代碼
  1. 由於開啓子進程是不支持打包 exe 文件的,因此會不停向操做系統申請建立子進程,而 multiprocessing.freeze_support() 做用就是支持打包到 windows 的 exe 文件。
  2. 多進程的程序運行後,若是直接關閉控制檯窗口,那麼整個程序都會退出,若是是進入任務管理,單獨結束控制窗口的進程,若是子進程不是守護進程,那麼子進程仍是會繼續運行。
  3. 若是是多線程,則沒有這個問題,能夠直接打包。

pyinstaller 打包一個 exe 並加入內置圖片

編輯生成的 main.spec 文件,修改 datas 列表,添加數據的格式爲:datas = [('source_path1', 'exe_dir1'), ('source_path2', 'exe_dir2')],可使用通配符markdown

  • source_path: 資源文件
  • exe_dir: 把資源文件放在 exe 程序中的文件夾。能夠直接使用 . 表示把資源文件放在 exe 程序的頂級文件夾中。

最後須要在源代碼的資源路徑引用中進行以下修改:多線程

import os
import sys

def resource_path(relative_path):
    """Get absolute path to resource works for dev and for PyInstaller"""
    if getattr(sys, 'frozen', False):
        base_path = sys._MEIPASS
    else:
        base_path = os.getcwd()
    return os.path.join(base_path, relative_path)

pic_path = resource_path('pic.png')
複製代碼

pyinstaller 會將文件夾的路徑信息存儲在 sys.MEIPASS 中,當使用的是單文件打包的方式,sys.MEIPASS 的值就是程序運行時建立 _MEIxxxxxx臨時目錄的絕對路徑。路徑通常在 C:\Users\user\AppData\Local\Temp\_MEIxxxxxapp

修改好 .spec 文件和源代碼後,從新打包便可,pyinstaller main.spec

對於以 dir 方式進行打包,則只須要修改 .spec 文件,添加資源文件便可。

調用外部程序使用無命令行窗口模式會出現程序報錯

問題出在 subprocess 上面,簡單來講,打包關閉了命令行窗口,stdin, stdout 無處安放,參考如下代碼修改便可。

def run_stuff(command_line):
    output_filename = 'somefile.txt'
    output_file = open(output_filename, "w")

    if gui_mode:
        result = subprocess.call(command_line, shell=True, stdout=outputFile, stderr=subprocess.STDOUT)
    else:
        proc = subprocess.Popen(command_line, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
        proc.stdin.close()
        proc.wait()
        result = proc.returncode
        output_file.write(proc.stdout.read())
複製代碼

參考

PyInstaller打包詳解 pyinstaller 打包成 exe 遇到的一些坑 Python subprocess.call() fails PyInstaller Manual

相關文章
相關標籤/搜索