pyinstaller打包后会形成一个文件夹或单个的exe(可以用参数指定)。但不论是哪一种情况,都会包含一个exe文件,用户可以双击它运行该应用程序。
假如你要打包myscript.py,那么打包完成后运行这个myscript.exe,效果就是运行myscript.py后的效果。
默认情况下,打包会形成一个黑色的控制台(cmd的样子),也可以设置隐藏这个控制台。
这个控制台用于为python提供标准输入(stdin),标准输出(stdout),标准错误(stderr)。也就是说,这个控制台上显示了print函数的输出,用于接收input函数的输入,还会输出python的异常。
如果你隐藏了这个控制台,程序中的print就无法显示(但是不会报错),报错信息也无法被用户直接看到(pyinstaller有一些选项来控制显示异常,后文详解);需要注意的是,此时不能使用input,否则会报错:
RuntimeError: input(): lost sys.stdin
python文件有一种后缀名*.pyw,这样的程序执行时默认会隐藏控制台。如果将文件后缀命名为pyw,那么pyinstaller也会认为它隐藏了控制台,不需要通过额外的选项来指定。
当你制作GUI程序的时候,最好选择隐藏控制台,来提升用户体验。
打包后的文件可能会被反编译(即通过exe文件得到原来的代码),可以通过一些方法进行加密(后文详解)。
下面介绍一下打包完成后形成的文件夹。
这个文件夹的名字是你提供的,一般是你要求打包的python文件的名称。文件夹中包含一个exe文件,以及其他一些依赖文件(比如一些dll文件,可能还有你的应用所需要的图片等素材)。你只需要将该文件夹压缩就能发给别人运行了。
当你运行里面的exe文件后,pyinstaller其实只是启动了解释器,然后通过解释器运行你的主程序。
打包成单个文件夹的形式便于调试,因为你可以清楚地看到pyinstaller将哪些模块文件放到了文件夹中。
当你更改代码,需要用户更新应用时,只需要让用户对于部分内容进行修改。如果你只修改了主程序,没有使用多余的模块,那么就只需要让用户替换里面的exe文件,而不用全部替换(因为更新前后使用的模块是一致的,它们都以多文件的形式放到了文件夹中)。
单个文件夹的状态下,程序的启动速度和打包前差不多。
打包成单个的文件夹后,文件大小可能会更大一些,因为大部分依赖文件没有进行压缩。
单个exe模式下,pyinstaller只会生成一个单独的exe文件,所有的依赖文件都会被压缩到exe文件中。
和上面的文件夹模式类似,exe启动后,pyinstaller也是通过调用python解释器来运行主程序的。
启动单个exe非常简单,用户只需要点击exe文件就能运行,而无需在一大堆的依赖文件中找到exe文件。并且在经过压缩后,这个exe文件的文件大小会大大减小。
单个exe的启动速度比较慢(通常会慢几秒,且只是启动时的速度,不是运行后的速度),这是因为pyinstaller会在这一段时间中将一些依赖文件写入到一个临时的文件夹(后文介绍该文件夹的调用方式)。
如果你希望添加一些附带文件(比如使用说明README),你还需要额外新建文件夹并将其放进去。
在了解相关原理后,下面正式进入打包环节。
本章介绍通过命令行参数进行打包,这种方式比较初级,适用于一般的打包方式。
打包需要通过cmd进行,语法和大多数工具一样。pyinstaller最简单的打包方式是:
pyinstaller myscript.py
其中myscript.py是你想要打包的程序。
如果这一步提示找不到myscript.py,请检查路径是否正确;如这一步提示找不到pyinstaller工具,请参考最后一章“常见问题”。
如果直接传递文件名,pyinstaller会生成一个spec文件将一些打包参数放到里面,然后进行打包。打包完成后,你会在你的目录下找到一个dist文件夹,里面存储了打包后的结果。pyinstaller还会生成一个build文件夹并写入一些日志信息。
当然,你也可以自己构建一个*.spec文件(后文介绍),然后交给pyinstaller进行处理。
本节只是列举并简要介绍常用的参数,并不过多展开,将在下面的部分对于一些重点参数举例介绍。
如有不熟悉命令行参数和命令行使用的读者可自行搜索,或者参考下面的介绍:
pyinstaller -D -i "icon.ico" myscript.py
调用命令时,首先给出工具名称(比如上面的 pyinstaller ),然后提供相关参数,有一些参数是可选的但不需要附带任何值(比如上面的 -D ),有一些参数是必选的(比如上面的 myscript.py ),有一些参数需要附带一个值(比如上面的 -i “icon.ico” )。其中有一些参数可以简写(比如 -i 就是 --icon 的简写)。
位置参数在打包时放在最后,而且无需指定关键字。
pyinstaller的位置参数是需要打包的文件路径,或是spec文件路径。
下面是比较有用的参数,读者可以自行了解,也可以跳过这部分,打包时用于参考:
参数名 | 描述 |
-D | 文件夹模式。在打包完成后生成一个文件夹,其中包含一个exe文件和一个包含若干依赖文件的文件夹(详见上文)。(默认) |
-F | 单文件模式。在打包完成后只会生成一个单独的exe文件(详见上文)。 |
–add-data |
指定一个文件夹或文件(非二进制),将其嵌入到exe中。 |
–add-binary |
和–add-data类似,不过指定的文件夹或文件是二进制的 |
-p DIR --paths DIR | 提供一个路径进行搜索并且导入里面的模块(不同的路径使用路径分隔符os.pathsep分隔开,或者多次使用这个参数)。 这可以解决有时候第三方模块找不到的问题。 |
–hidden-import MODULENAME --hiddenimport MODULENAME | 需要进行额外导入的模块。当pyinstaller在程序中找不到一些模块时,需要你额外指定。这个参数可以多次使用,可以解决一些模块找不到的问题。 |
–splash IMAGE_FILE | 添加一个启动画面(图片文件)路径,在程序运行前显示指定的启动图片,起到加载提示的效果。 |
-c, --console, --nowindowed | 打包程序运行后出现一个黑色的控制台窗口(默认) |
-w, --windowed, --noconsole | 打包程序运行后隐藏控制台窗口 |
-i |
设置打包后exe程序的图标(只能在Windows和macOS上使用) |
–disable-windowed-traceback | 禁用异常提示(只能在Windows和macOS上使用) |
–help, -h | 打印pyinstaller的帮助信息并退出 |
下面是一个程序示例,将创建一个窗口并显示一张图片image.gif和一段提示。读者无需了解其代码细节。接下来将以这个程序为例进行一个简单的打包示范。
'''
一个简单的应用
'''
import tkinter as tk # 导入tkinter
root = tk.Tk() # 创建窗口
root.title("我的应用程序") # 更改标题
image = tk.PhotoImage(file="assets/image.gif")
label = tk.Label(root, text="你好,用户!", image=image, compound="top")
label.pack() # 显示图片
root.mainloop() # 保持窗口运行