Francky380
Francky380

Reputation: 233

Create executable with python 3.7 PyQt5 and cx_Freeze but DLL Failed to load

I developed a "not so simple" GUI with PyQt5 via Anaconda 3 (Python 3.7) and Designer. I have 3 different .ui files that I import in my program.

When I run cx_Freeze, everything runs good, I create the .exe. Then, I copy the "platform" folder from my "Python" folder in the "Build" folder that cx_Freeze creates.

BUT, when I pass it to an other machine without anything on it (no anaconda, no python, no cx_Freeze, nothing), the app doesn't run. I get:

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

It happens in the 10th line of my code which is:

from PyQt5 import QtGui, QtWidgets

The imports in my code are:

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

design, flex and entry are the .ui files. They all contain this part at the end (don't know if it helps):

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_())

And finally, the setup file I run with 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)])

I read a lot about it, but I feel that there is no "miracle" solution...

Could you help me freeze my app and make it run on a "virgin machine" ?

Upvotes: 2

Views: 3121

Answers (2)

LoneWanderer
LoneWanderer

Reputation: 3341

I faced a similar problem recently, with the following versions:

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

Symptoms :

The cx_Freeze package was successful and execution working fine on my machine, because (as I found later and explained below) I had Python 3.6 x64 installed on my machine and visibile via environement variables.

On an another machine, the package failed with the exact same error on first PyQt5 import:

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

However, in my case, all the necessary dlls seemed all in the right place :

  • cx_Freeze seemed to have correctly put python3.dll and python36.dll next to the executable. But no other dll were copied there (no vcruntime140.dll for instance)
  • all the necessary python modules were in place, including PyQt5 and all its dlls

Solution that was working :

(I created a cx_Freeze issue)

Contrary to the other answer, I had to copy

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

into the following folders:

<cx_freeze_build_dir_target>/lib/PyQt5/

The corresponding cx_Freeze setup config for this would be to add this to the include_files list, in the following fashion.

Unfortunatly, this does not work due to cx_Freeze having an sort of exception file liste that includes python3.dll and prevents the actual copy via include_files. So, the copy should be performed manually, after full setup script execution...

# 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
                    }

Upvotes: 0

jpeg
jpeg

Reputation: 2461

You might be facing Issue #504 of the cx_Freeze repository. In that case, quoting a comment by marceloduarte there:

The workaround is to copy python3.dll to the directory where the executable was made. cx_Freeze copies python37.dll, but does not copy python3.dll. The vcruntime140.dll may also have to be copied when it no longer exists on the system.

First try to manually copy these DLLs (you find them in the directory of your python installation containing python.exe) to the directory of your executable. If that solves the problem, you can tell cx_Freeze to do it for you by using the include_files list of the build_exe_options. Modify you setup script as follows:

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"]}

Maybe you need to copy further DLLs, such as msvcp140.dll, or any other DLL present inside the site-packages/PyQt5 directory (including subdirectories) of your Python installation.

Upvotes: 1

Related Questions