Search code examples
pythonpyqtpyqt5pyinstallerqt-quick

PyInstaller / QtQuick: Could not find qmake version 5.x


Windows 10, Python 3.5, PyQt5 is installed via Pip, pyinstaller 3.2.1 also installed via Pip. I can make a little PyQt5 qml app that works fine when run normally under Python.

If I have a Python module called test.py that contains:

from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView

and do:

pyinstaller main.py

I get:

3452 ERROR: Could not find qmake version 5.x, make sure PATH is set correctly or try setting QT5DIR.
3452 ERROR: Cannot find QT_INSTALL_QML directory, "qmake -query QT_INSTALL_QML" returned nothing

followed by:

Traceback (most recent call last):
  File "C:\Python35\Scripts\pyinstaller-script.py", line 11, in <module>
    load_entry_point('PyInstaller==3.2.1', 'console_scripts', 'pyinstaller')()
  File "c:\python35\lib\site-packages\PyInstaller\__main__.py", line 90, in run
    run_build(pyi_config, spec_file, **vars(args))
  File "c:\python35\lib\site-packages\PyInstaller\__main__.py", line 46, in run_build
    PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs)
  File "c:\python35\lib\site-packages\PyInstaller\building\build_main.py", line 788, in main
    build(specfile, kw.get('distpath'), kw.get('workpath'), kw.get('clean_build'))
  File "c:\python35\lib\site-packages\PyInstaller\building\build_main.py", line 734, in build
    exec(text, spec_namespace)
  File "<string>", line 16, in <module>
  File "c:\python35\lib\site-packages\PyInstaller\building\build_main.py", line 212, in __init__
    self.__postinit__()
  File "c:\python35\lib\site-packages\PyInstaller\building\datastruct.py", line 161, in __postinit__
    self.assemble()
  File "c:\python35\lib\site-packages\PyInstaller\building\build_main.py", line 470, in assemble
    module_hook.post_graph()
  File "c:\python35\lib\site-packages\PyInstaller\building\imphook.py", line 409, in post_graph
    self._load_hook_module()
  File "c:\python35\lib\site-packages\PyInstaller\building\imphook.py", line 390, in _load_hook_module
    attr_value = sanitizer_func(attr_value)
  File "c:\python35\lib\site-packages\PyInstaller\building\utils.py", line 466, in format_binaries_and_datas
    src_root_path_or_glob))
FileNotFoundError: Path or glob "Qt" not found or matches no files.

If I comment out the second line from test.py (the one about QtQuick) PyInstaller succeeds.

I can't make the path point to qmake because I don't have qmake on my machine. I don't really understand why I need qmake?


Solution

  • With Python 3.4.4 and PyQt5 5.5.1 (from https://sourceforge.net/projects/pyqt/files/PyQt5/PyQt-5.5.1/ - I think this is the last PyQt5 for 3.4) and PyInstaller 3.2.1 this all works fine out of the box.

    The difference I think is that the PyQt5 directory structure under Lib\site-packages\PyQt5 is very different between 3.4 and 3.5 and I guess PyInstaller isn't coping with that yet. In particular, Python34 has the Qt5 binaries (Qt5Core.dll etc) in the root of Lib\site-packages\PyQt5 whereas in Python35 they end up tucked away under Lib\site-packages\PyQt5\Qt\bin. There are a lot of other differences too.

    Anyway, the easy answer is therefore use Python34 / PyQt5.5.1. FWIW I have sort of got it working with Python35 and latest PyQt5 (5.8.1), but it's a bodge. I have both Python34 and Python35 installed, and the following (in this order) in my Windows path:

    C:\Python35\
    C:\Python35\Scripts\
    C:\Python35\Lib\site-packages\PyQt5\Qt\bin
    C:\Python34\Lib\site-packages\PyQt5
    

    The Python34 line has to be there otherwise we get the Could not find qmake version 5.x stuff, and the Python35\Lib\site-packages\PyQt5\Qt\bin path being above that seems to make it pick up the correct binaries.

    Doing this, pyinstaller reports success, but when I run I get a failure about Cannot load library ... dist\main\qml\QtQuick\Controls\qtquickcontrolsplugin.dll: The specified procedure could not be found. This can be solved by manually copying over Python35\Lib\site-packages\PyQt5\Qt\qml\QtQuick\Controls\qtquickcontrolsplugin.dll to dist\main\qml\QtQuick\Controls. After that my app runs as expected.

    I haven't found a way to make pyinstaller pick up the correct qtquickcontrolsplugin.dll automatically.

    I guess leaving C:\Python34\Lib\site-packages\PyQt5 in the path it's probably picking up some other wrong files from Python34, and if that's the case you can copy them over manually or try to fiddle with paths etc. to try to make the right thing happen, but I'm just going to use Python34 until PyInstaller gets fixed.