cx_Freeze 打包 Python3.4+PyQt5.3

基于 Python 打包成 exe 的工具,主要有这样几个:

但遗憾的是,目前只有 cx-Freeze 明确表示支持 Python3 。pyinstaller 则有一个试验性分支在进行这方面的测试。

我有一个项目 data_tester 使用 Python3.4+PyQt5.3 开发,需要使用 cx-Freeze 将其打包成 exe 格式,提供给其它同事在 Windows 7(64bit) 系统中使用。

具体打包的方法,查看 cx-Freeze 官方文档 即可知晓,这篇文章中仅仅记述我踩过的几个坑。

_fix_up_module

打包成功后,运行出现这样的错误:

_fix_up_module

这是因为,在 PiPy 上安装的 cx-Freeze 有问题 。需要在 这里下载一个独立的 Windows 编译版本 进行安装。

由于我们使用的是 64bit 系统,我下载的是 cx_Freeze‑4.3.3.win‑amd64‑py3.4.exe ,双击安装即可。注意安装前先卸载在 PyPi 上安装的 cx-Freeze 包。

No such file or directory

使用 exe 版本的 cx-Freeze 打包成功后,运行时又出现新的错误:

No such file or directory

在程序中,我们经常需要获取项目所在的当前路径,用来读取一些配置文件或资源。

data_tester 中,我需要读取一个位于 hhl 包中的 dt.conf 资源文件。下面是读取代码:

:::python
workDir = os.path.abspath(os.path.join(
    os.path.split(os.path.abspath(__file__))[0], os.pardir))
templFile = os.path.join(workDir, 'hhl', 'dt.conf')

而在 exe 版本中,由于所有的 python 代码都被打包到 library.zip 文件中了,__file__ 的值就变成了 library.zip 中的相关路径。这样去取一个资源文件,显然是取不到的。

还有一个问题,就是 cx-Freeze 并不会将非 python 文件自动打包到最终的 exe 所在的文件夹中。我们需要在 setup.py 中指定我们需要打包的资源文件。这些指定的文件会被直接复制到 exe 所在的文件夹中,而不会被打包到 library.zip 中。

:::python
options = {
'build_exe':{'packages':packages, 'include_files':['hhl/dt.conf']},
}

这样进行处理之后,dt.conf 这个资源文件在 exe 版本中的路径和非 exe 版本中的路径就不同了。我们需要判断当前执行的是否是 exe 版本,根据结果来读取不同路径的 dt.conf

下面的代码进行了这样的处理:

#!/usr/bin/env python
# 当位于 exe 版本中时,可以使用 os.sys.executable 来获取当前执行文件的路径
if hasattr(sys, 'frozen'):
    workDir = os.path.dirname(os.path.join(os.sys.executable))
    templFile = os.path.join(workDir, 'dt.conf')
else:
    workDir = os.path.abspath(os.path.join(
        os.path.split(os.path.abspath(__file__))[0], os.pardir))
    templFile = os.path.join(workDir, 'hhl', 'dt.conf')

这是完整的 setup.py 文件:

#!/usr/bin/env python

import sys
from cx_Freeze import setup, Executable

base = None

if sys.platform == 'win32':
    base = 'Win32GUI'

packages = [
        'hhl',
        'hhl.protos',
        'hhl.components',
        'hhl.libs.helper',
        'hhl.libs.net',
        ]

options = {
'build_exe':{'packages':packages, 'include_files':['hhl/dt.conf']},
}

executables = [Executable('main.py', base=base, targetName='datatester.exe')]

setup(
    name="datatester",
    version="1.0.0",
    url='http://zengrong.net/',
    author='zrong',
    author_email='zrongzrong@gmail.com',
    description="A data tester for HHL.",
    options=options,
    executables=executables,
)

Could not load the Qt platform plugin "windows"

经历了上面两个坑,在打包的电脑上,运行成功了。但是把打包结果复制到其它电脑上运行时,出现了这样的错误:

Could not load the Qt platform plugin "windows"

python 3.3, pyqt5 and cx_freeze exe on 64-bit windows 一文中提到,将 libEGL.dll 复制到 exe 相同目录可以解决这个问题。这个文件可以在 PyQt5 的安装文件夹中找到。