使用 python 3.7 PyQt5 和 cx_Freeze 创建可执行文件,但 DLL 无法加载

Posted

技术标签:

【中文标题】使用 python 3.7 PyQt5 和 cx_Freeze 创建可执行文件,但 DLL 无法加载【英文标题】:Create executable with python 3.7 PyQt5 and cx_Freeze but DLL Failed to load 【发布时间】:2019-09-19 20:50:31 【问题描述】:

我通过 Anaconda 3 (Python 3.7) 和 Designer 使用 PyQt5 开发了一个“不那么简单”的 GUI。 我在我的程序中导入了 3 个不同的 .ui 文件。

当我运行 cx_Freeze 时,一切运行良好,我创建了 .exe。然后,我从 cx_Freeze 创建的“Build”文件夹中的“Python”文件夹中复制“platform”文件夹。

但是,当我将它传递给另一台没有任何东西的机器(没有 anaconda,没有 python,没有 cx_Freeze,什么都没有)时,应用程序无法运行。我明白了:

ImportError: DLL load failed: The specified module could not be found

它发生在我的代码的第 10 行,即:

from PyQt5 import QtGui, QtWidgets

我的代码中的导入是:

from PyQt5 import QtGui, QtWidgets
import sys
import glob
import datetime
from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
import numpy as np
import smtplib

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

import design
import flex
import entry

designflexentry 是 .ui 文件。他们都在最后包含这部分(不知道是否有帮助):

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Dialog = QtWidgets.QDialog()
    ui = Ui_Dialog()
    ui.setupUi(Dialog)
    Dialog.show()
    sys.exit(app.exec_())

最后,我使用 cx_Freeze 运行的设置文件:

import sys
from cx_Freeze import setup, Executable
import matplotlib
import numpy


# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = "packages": ["os", "matplotlib"], "includes": ["PyQt5", "atexit"], "excludes": ["tkinter"]

# GUI applications require a different base on Windows (the default is for a
# console application).
base = None
if sys.platform == "win32":
    base = "Win32GUI"

setup(  name = "Flexicounts",
        version = "0.1",
        description = "Flexicounts pour faire tes comptes facilement",
        options = "build_exe": build_exe_options,
        executables = [Executable("flexicounts.py", base=base)])

我读了很多关于它,但我觉得没有“奇迹”的解决方案......

你能帮我冻结我的应用程序并让它在“原始机器”上运行吗?

【问题讨论】:

【参考方案1】:

您可能会面对 cx_Freeze 存储库的 Issue #504。在这种情况下,引用 marceloduarte 的评论:

解决方法是将python3.dll 复制到生成可执行文件的目录。 cx_Freeze 复制python37.dll,但不复制python3.dllvcruntime140.dll 在系统上不再存在时也可能需要复制。

首先尝试手动将这些 DLL(您可以在包含 python.exe 的 python 安装目录中找到它们)复制到可执行文件的目录中。如果这解决了问题,您可以使用build_exe_optionsinclude_files 列表告诉cx_Freeze 为您做这件事。修改你的设置脚本如下:

import os
python_dir = os.path.dirname(sys.executable)  # directory of your python installation
build_exe_options = "packages": ["os", "matplotlib"], 
                     "includes": ["PyQt5", "atexit"],
                     "include_files": [os.path.join(python_dir, "python3.dll"), os.path.join(python_dir, "vcruntime140.dll")], 
                     "excludes": ["tkinter"]

也许您需要复制更多的 DLL,例如 msvcp140.dll,或 Python 安装的 site-packages/PyQt5 目录(包括子目录)中存在的任何其他 DLL。

【讨论】:

它工作得很好!谢谢。我按照您的建议编辑了设置文件并且它有效。这是我的“神奇”解决方案。感谢您对我的帖子的编辑顺便说一句。我不是英语母语。 @Francky380 很高兴能帮助您解答和编辑问题。顺便说一句,我每次编辑都会获得两个声誉点,这被评论者或帖子所有者认为是有用的,当一个新的 SO 新手时,这是获得声誉点的一种很好且有用的方法。我也不是英语母语,但你可能会说法语,从你的应用程序的description 推断:-)【参考方案2】:

我最近遇到了类似的问题,有以下版本:

python 3.6.6 x64
cx-Freeze==6.10
PyQt5==5.15.4
PyQt5-Qt5==5.15.2
PyQt5-sip==12.9.0
PyQt5-stubs==5.15.2.0
PyQtWebEngine==5.15.5
PyQtWebEngine-Qt5==5.15.2

症状:

cx_Freeze 包成功,并且在我的机器上运行良好,因为(我后来发现并在下面解释)我的机器上安装了 Python 3.6 x64,并且通过环境变量可见。

在另一台机器上,包在第一次 PyQt5 导入时失败并出现完全相同的错误:

ImportError: DLL load failed: The specified module could not be found

但是,就我而言,所有必要的 dll 似乎都在正确的位置:

cx_Freeze 似乎已正确地将python3.dllpython36.dll 放在可执行文件旁边。但是那里没有复制其他 dll(例如没有 vcruntime140.dll) 所有必要的 Python 模块都已到位,包括 PyQt5 及其所有 dll

有效的解决方案:

(我创建了一个cx_Freeze issue)

与other answer相反,我不得不复制

python3.dll
(either from <cx_freeze_build_dir_target> or from python3.6.6 install dir ...)
(python36.dll works too but is much bigger)

进入以下文件夹:

<cx_freeze_build_dir_target>/lib/PyQt5/

为此,相应的 cx_Freeze 设置配置将按以下方式将其添加到 include_files 列表中。

不幸的是,这不起作用,因为 cx_Freeze 具有一种包含 python3.dll 的异常文件列表,并阻止通过 include_files 进行实际复制。因此,应该手动执行复制,完整的设置脚本执行之后...

# import pkgutil
from pathlib import Path
from glob import glob
import sys
# ... your stuff
# 
includes_list = []
# your stuff
# ...
# pyqt5 force copy of pythonlib(s) into qt5 target dir
# may not work if you intend to zip pyqt5 ?
python_dir = Path(sys.executable).parent
python_dlls = [ Path(p) for p in glob(f"python_dir.as_posix()/python*.dll")]
pyqt_base_dir = Path("lib", "PyQt5")
# prepare cx_Freeze tuples (source file, target dir)
includes_list+= [ (p, pyqt_base_dir / p.name) for p in python_dlls ]
build_exe_options = "packages"     : ...,           # your packages
                     "includes"     : ...,           # yours
                     "include_files": includes_list,
                     "excludes"     : ...            # yours
                    

【讨论】:

以上是关于使用 python 3.7 PyQt5 和 cx_Freeze 创建可执行文件,但 DLL 无法加载的主要内容,如果未能解决你的问题,请参考以下文章

使用 cx_Freeze 冻结 Python 3.7 脚本时如何修复错误

无法为 Python 3.7 64 位安装 cx_Freeze 或 scipy

cx_Freeze 错误模块 SSL 不可用 Python 3.7 Windows 10

Python 3 + pyQt5 + cx_freeze

在 PyQt5 中使用 cx_freeze,找不到 PyQt5

没有模块 QtMultimedia [Mac OS - Anaconda - Python 3.7 - PyQt5]