用Python写了一个小工具,想打包给其他人用,但是他们电脑上不一定都有环境。于是想打包成可执行文件,点开就能运行。于是就使用Pyinstaller打包,中间遇到了很多问题。
首先,这是他们的官网,基本的用法这里都能找到,还有很多细节问题可能就需要百度了。
先了解Pyinstaller的基本用法,pyinstaller main.py这样就能直接打包一个文件为可执行文件。这是最基本最简单的用法,如果你的文件不是很复杂的话(比如只输出“你好时间”),用这个就够了。复杂一点就需要添加参数比如-F -D指定单文件还是单文件夹格式。
不过最好还是使用spec配置文件去指定配置,这样就不用每次去修改一大长串的命令了。下面我使用的也都是spec配置文件的方法。
Spec配置文件
本质上使用pyinstaller main.py也是会生成main.spec文件的,因此我们可以先这样得到spec文件,然后修改文件,再运行pyinstaller main.spec来执行打包。
下面是一个spec文件模板,本质上这玩意就是py文件,只是拓展名不同,所以也能在这里面写一些代码。
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
# 变量 a 是一个 Analysis 对象
# 把要打包的脚本传给他,初始化的过程中,他会分析依赖情况
# 最后会生成 a.pure a.scripts a.binaries a.datas 这样4个关键列表,以及 a.zipped_data (不重要)
# 其中:
# a.pure 是依赖的纯 py 文件,
# a.scripts 是要依次执行的脚本文件,
# a.binaries 是依赖的二进制文件,
# a.datas 是要复制的普通文件
# 分析的这一步最耗时间
# 这一部分负责收集你的脚本需要的所有模块和文件。的;hiddenimports 参数可以指定一些 PyInstaller 无法自动检测到的模块。
a = Analysis(
['pix2text_app.py'],
pathex=[], # 用来指定模块搜索路径
binaries=[], # 包含了动态链接库或共享对象文件,会在运行之后自动更新,加入依赖的二进制文件
datas=[('config.yaml', '.')], # 列表,用于指定需要包含的额外文件。每个元素都是一个元组:(文件的源路径, 在打包文件中的路径)
hiddenimports=[], # 用于指定一些 PyInstaller 无法自动检测到的模块
hookspath=['./hook'], # 指定查找 PyInstaller 钩子的路径
hooksconfig={}, # 自定义 hook 配置,这是一个字典,一行注释写不下,此处先不讲
runtime_hooks=[], # 指定运行时 hook,本质是一个 Python 脚本,hook 会在你的脚本运行前运行,可用于准备环境
excludes=[], # 用于指定需要排除的模块
noarchive=False,
optimize=0,
)
splash = Splash('splash.jpg',
binaries=a.binaries,
datas=a.datas,
text_pos=(10, 50),
text_size=12,
text_color='black') # 设置启动画面
# 变量 pyz 是一个 PYZ 对象,默认给他传入 a.pure 和 a.zipped_data# 初始化过程中,它会把 a.pure a.zipped_data 打包成一个 pyz 文件
# 如果有密码,还会加密打包
# pyz 是指生成的可执行文件的名称。它是由 PyInstaller 用来打包 Python 程序和依赖项的主要文件。
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
# 变量 exe 是一个 EXE 对象,
# 给它传入打包好的 pyz 文件、a.scripts、程序名、图标、是否显示Console、是否debug
# 最后他会打包生成一个 exe 文件
# 创建 exe 文件
exe = EXE(
pyz,
a.scripts,
splash,
[],
contents_directory='.',
exclude_binaries=True, # 若为 True,所有的二进制文件将被排除在 exe 之外,转而被 COLLECT 函数收集
name='pix2text_app',
debug=False,
bootloader_ignore_signals=False,
strip=False, # 是否移除所有的符号信息,使打包出的 exe 文件更小
upx=True, # 是否用 upx 压缩 exe 文件
console=False, # 若为 True 则在控制台窗口中运行,否则作为后台进程运行
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
# 变量 coll 是一个 COLLECT 对象,
# 给它传入:
# exe
# a.binaries 二进制文件
# a.dattas 普通文件
# name 输出文件夹名字
# 在实例化的过程中,会把传入的这些项目,都复制到 name 文件夹中
# 这个对象包含了所有需要分发的文件
# 包括 EXE 函数创建的 exe 文件、所有的二进制文件、zip 文件(如果有的话)和数据文件
coll = COLLECT(
exe,
splash.binaries,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='pix2text_app',
)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
钩子
当项目变得复杂的时候,就会出现一些问题,比如有一些第三方包的资源文件不存在报错。这大概是因为pyinstaller默认不打包这些,所以就需要一些钩子去指定打包他们。官方集成了一些常用包的钩子文件,但是如果使用的包比较小众,可能就需要自己手动写。比如我是用的cnocr包里面就有一个label_cn.txt需要打包。可以在项目根目录下新建一个hook文件夹,然后再在里面新建一个hook-[模块名称].py的hook文件。这样打包的时候就会自动把资源文件也加进去了。
from PyInstaller.utils.hooks import copy_metadata, collect_data_files
datas = copy_metadata('cnocr')
datas.extend(collect_data_files('cnocr'))2
3
4
Splash启动图
如果应用程序打开很慢,就需要一个启动图来提示用户应用正在打开。方法就是在spec文件里面添加。具体代码在上面的模板文件里就有。这个是官方文档Using Spec Files — PyInstaller 6.8.0 documentation。不过我在使用的时候会出现程序已经启动了,但是启动图还在的问题暂时还不知道怎么解决。