使用pyinstaller将Python脚本打包为exe

最近使用PyQt 5写了一个带界面的Python脚本,记录下几个过程。

1、PyQt5使用ui编辑器界面

  先使用Qt的designer编辑界面,设界面为appForm.ui,以下命令将Form转换为Python脚本:

1
pyuic5 appForm.ui -o appForm.py

2、将py脚本打包成exe

  标准的pip安装:pip install -U installer,当前安装的版本为3.2.1,安装完成后会出现两个错误,以下分别解决。

  直接使用pip安装出错,这里使用whl包离线安装,下载地址:lfd,当前版本为3.4(2019.2.26)。命令:

1
pip install PyInstaller-3.4-py2.py3-none-any.whl

2.1 错误:SyntaxError: 'yield' inside async function

  根据这里修正,主要包含以下两个文件的修改:   1) 修改PyInstaller/building/api.py文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
     logger.info("Building PYZ (ZlibArchive) %s", self.name)
# Do not bundle PyInstaller bootstrap modules into PYZ archive.
toc = self.toc - self.dependencies
- for entry in toc:
+ for entry in toc[:]:
if not entry[0] in self.code_dict and entry[2] == 'PYMODULE':
# For some reason the code-object, modulegraph created
# is not available. Recreate it
- self.code_dict[entry[0]] = get_code_object(entry[0], entry[1])
+ try:
+ self.code_dict[entry[0]] = get_code_object(entry[0], entry[1])
+ except SyntaxError:
+ # Exclude the module in case this is code meant for a newer Python version.
+ toc.remove(entry)
# sort content alphabetically to support reproducible builds
toc.sort()

  2) 修改PyInstaller/lib/modulegraph/modulegraph.py文件

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
     m = self.createNode(cls, fqname)
m.filename = pathname
if co is not None:
- if isinstance(co, ast.AST):
- co_ast = co
- co = compile(co_ast, pathname, 'exec', 0, True)
- else:
- co_ast = None
- self._scan_code(m, co, co_ast)
+ try:
+ if isinstance(co, ast.AST):
+ co_ast = co
+ co = compile(co_ast, pathname, 'exec', 0, True)
+ else:
+ co_ast = None
+ self._scan_code(m, co, co_ast)
...
- if self.replace_paths:
- co = self._replace_paths_in_code(co)
- m.code = co
+ if self.replace_paths:
+ co = self._replace_paths_in_code(co)
+ m.code = co
+ except SyntaxError:
+ self.msg(2, "load_module: SynaxError in ", pathname)
+ cls = InvalidSourceModule
+ m = self.createNode(cls, fqname)

self.msgout(2, "load_module ->", m)
return m

2.2 替换plugins

  由于本脚本仅使用了PyQt5,而打包时会打包PyQt4,造成程序无法运行。可以作如下修正。进入${Python}$/Lib/site-packages/PyInstaller/loader/rthooks目录,将pyi_rth_qt4plugins.py备份后删除,复制一份pyi_rth_qt5plugins.py并重命名为pyi_rth_qt4plugins.py

  安装完成直接使用。

2.3 pyinstaller的使用

  详细的命令,可以使用pyinstaller --help获得,通常使用如下命令打包: pyinstaller app.py appForm.py --hidden-import=PyQt4 --hidden-import=Matplotlib --noconsole

1
2
pyinstaller app.py appForm.py --noconsole

  直接运行上述命令,会出现:Cannot find existing PyQt5 plugin directories错误,这时按照提示的位置创建路径(测试时提示路径为:C:/qt/_h_env/Library/plugins),并将pyqt5.dllpyqt5qmlplugin.dll文件复制到该路径即可。

  再次执行上述命令,即可正常生成exe文件。此时exe文件因缺少platformsqwindows.dll文件而无法正常运行,在exe同目录下新建一个platforms,将同目录下的qwindows.dll移至这个目录即可。