pyinstaller

简述

pyinstaller 打包的流程:读取编写好的 python 脚本,分析其中调用的模块和库,然后收集这些文件的副本(包括 Python 的解释器)。最后把副本与脚本,可执行文件等放在一个文件夹中,或者可选地封装在一个可执行文件中。

基本使用方法

安装 pyinstaller

pip install pyinstaller

生成 spec 文件

进入主程序目录,输入 pyi-makespec -w main.py 生成 main.spec 文件。

几个常用参数

参数 说明
-F,-onefile 打包一个单个文件
-D,-onedir 打包多个文件,在 dist 中生成很多依赖文件
-w,-windowed,-noconcole 当程序启动的时候不会打开命令行(只对windows有效)

根据需要编辑 spec 文件

onefile 模式

# -*- 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 模式

# -*- 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.exe

常见问题

打包多进程、线程

用 pyinstaller 打包好 exe 后,双击运行,会出现无限循环地进入主程序的情况。此时需要在调用多进程的前面加上如下的代码:

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')],可以使用通配符

  • 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\_MEIxxxxx

修改好 .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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容