python打包文件之pyinstaller

桌面应用想要使用就要先进行打包,python扩展包pyinstaller帮我们提供了打包功能,使用过程中你会发现很多坑,后面解释。

pyinstaller常用参数

说明

以下介绍假设用户脚本为main.py,生成结果为main.exe
有些参数实在很少用到,只列出简单介绍,具体请参阅原文。

1
2
3
以下介绍假设用户脚本为main.py,生成结果为main.exe。
有些参数实在很少用到,只列出简单介绍,具体请参阅原文。
原文(英文): https://pyinstaller.readthedocs.io/en/v3.3.1/usage.html

通用参数

参数名描述说明
-h显示帮助
-v显示版本号
–distpath生成文件放在哪里默认:当前目录的dist文件夹内
–workpath生成过程中的中间文件放在哪里默认:当前目录的build文件夹内
-y如果dist文件夹内已经存在生成文件,则不询问用户,直接覆盖默认:询问是否覆盖
–upx-dir UPX_DIR指定upx工具的目录默认:execution path
-a不包含unicode支持默认:尽可能支持unicode
–clean在本次编译开始时,清空上一次编译生成的各种文件默认:不清除
–log-level LEVEL控制编译时pyi打印的信息一共有6个等级,由低到高分别为TRACE DEBUG INFO(默认) WARN ERROR CRITICAL。也就是默认清空下,不打印TRACEDEBUG信息

与生成结果有关的参数

参数名描述说明
-D生成one-folder的程序(默认)生成结果是一个目录,各种第三方依赖、资源和exe同时存储在该目录
-F生成one-file的程序生成结果是一个exe文件,所有的第三方依赖、资源和代码均被打包进该exe
–specpath指定.spec文件的存储路径默认:当前目录
-n生成的.exe文件和.spec的文件名默认:用户脚本的名称,即main.pymain.spec

指定打包哪些资源、代码

参数名描述说明
–add-data打包额外资源用法:pyinstaller main.py --add-data=src;destwindows以;分割,linux以:分割
–add-binary打包额外的代码用法:同–add-data。与–add-data不同的是,用binary添加的文件,pyi会分析它引用的文件并把它们一同添加进来
-p指定额外的import路径,类似于使用PYTHONPATH参见PYTHONPATH
–hidden-import打包额外pypyi在分析过程中,有些import没有正确分析出来,运行时会报import error,这时可以使用该参数
–additional-hooks-dir指定用户的hook目录hook用法参见其他,系统hookPyInstaller\hooks目录下
–runtime-hook指定用户runtime-hook如果设置了此参数,则runtime-hook会在运行main.py之前被运行
–exclude-module需要排除的modulepyi会分析出很多相互关联的库,但是某些库对用户来说是没用的,可以用这个参数排除这些库,有助于减少生成文件的大小
–keypyi会存储字节码,指定加密字节码的key16位的字符串

生成参数

参数名描述说明
-d执行生成的main.exe时,会输出pyi的一些log,有助于查错默认:不输出pyilog
-s优化符号表原文明确表示不建议在windows上使用
–noupx强制不使用upx默认:尽可能使用。

其他

参数名描述说明
–runtime-tmpdir指定运行时的临时目录默认:使用系统临时目录

Windows和Mac特有的参数

参数名描述说明
-c显示命令行窗口与-w相反,默认含有此参数
-w不显示命令行窗口编写GUI程序时使用此参数有用。
-imain.exe指定图标pyinstaller -i beauty.ico main.py

Windows特有的参数

参数名描述说明
–version-file添加版本信息文件pyinstaller --version-file ver.txt
-m, --manifest添加manifest文件pyinstaller -m main.manifest
-r RESOURCE请参考原文
–uac-admin请参考原文
–uac-uiaccess请参考原文

常见问题

Failed to execute script main

这个问题解决这个问题的方法很多,主要看你对应的报错信息,本人报错提示为:ImportError: unable to find Qt5Core.dll on PATH

https://stackoverflow.com/questions/56949297/how-to-fix-importerror-unable-to-find-qt5core-dll-on-path-after-pyinstaller-b

https://github.com/pyinstaller/pyinstaller/issues/4293

里面描述的比较详细,具体就不详说了。总的来说呢,就是有的版本pyqt5库对系统变量的加载存在bug,具体原因只有官方才能解释了,咱也没法管了,对有bug的版本库提供一种办法就是,在主程序中pyqt5import之前就对系统变量进行手动设置,import代码如下:

1
2
3
4
5
6
import sys, os
if hasattr(sys, 'frozen'):
os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH']
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import *
from untitled import Ui_MainWindow

这样就解决了该问题。

加入打包文件信息

1
pyinstaller -w -D --icon=icon.ico --version-file=file_version_info.txt main.py

file_version_info.txt保存的是文件信息

其格式大概如下:

1
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
# UTF-8
#
# For more details about fixed file info 'ffi' see:
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
VSVersionInfo(
ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0.
filevers=(10, 5, 3819, 400),
prodvers=(10, 5, 3819, 400),
# Contains a bitmask that specifies the valid bits 'flags'r
mask=0x17,
# Contains a bitmask that specifies the Boolean attributes of the file.
flags=0x0,
# The operating system for which this file was designed.
# 0x4 - NT and there is no need to change it.
OS=0x4,
# The general type of file.
# 0x1 - the file is an application.
fileType=0x1,
# The function of the file.
# 0x0 - the function is not defined for this fileType
subtype=0x0,
# Creation date and time stamp.
date=(0, 0)
),
kids=[
StringFileInfo(
[
StringTable(
u'040904b0',
[StringStruct(u'CompanyName', u'Tony'),
StringStruct(u'FileDescription', u'作者托小尼646547989@qq.com'),
StringStruct(u'FileVersion', u'1.0'),
StringStruct(u'InternalName', u'cutImg'),
StringStruct(u'LegalCopyright', u'Copyright 2019 Tony. All rights reserved.'),
StringStruct(u'OriginalFilename', u'cutimg.exe'),
StringStruct(u'ProductName', u'图片剪切'),
StringStruct(u'ProductVersion', u'1.0'),
StringStruct(u'CompanyShortName', u'Tony'),
StringStruct(u'ProductShortName', u'图片剪切'),
StringStruct(u'LastChange', u'b4ce371876a3dcbfef2affeee4ea9c9163d4628c-refs/branch-heads/3538@{#516}'),
StringStruct(u'Official Build', u'1'),
StringStruct(u'SpecialBuild', u'1023'),
StringStruct(u'PrivateBuild', u'10016'),
StringStruct(u'KernelBuild', u'129'),
StringStruct(u'ExpressBuild', u'1'),
StringStruct(u'KernelVersion', u'70.0.3538.25')])
]),
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
]
)

使用时可以复制以上代码,也可以参考别的exe文件生成一份

若之後版本更新不能使用的話

  1. 打開CMDCDPython安裝目錄下cd Lib/site-packages/PyInstaller-3.3.1-py3.6.egg/PyInstaller/utils/cliutils/
  2. 輸入python grab_version.py {拖曳任一安裝或免安裝的exe檔到CMD}
  3. 按下ENTER,會自動產生file_version_info.txt,再將內容修改成自己要的資訊就可以使用了!