Repository: pyinstaller/pyinstaller

Issue:
Python3.4 PyQt5 QML application requires environment variables
With the PyQt5 fixes merged https://github.com/pyinstaller/pyinstaller/pull/3439 I decided to try creating an executable that I have been having trouble with. https://github.com/pyinstaller/pyinstaller/pull/3439#issuecomment-379064155

This is the source code https://github.com/Siecje/qml-testing/tree/PyInstaller

I'm using
- Windows 7 32-bit
- Qt 5.10.1
- PyQt5 compiled from source
- Python 3.4.4
- pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip
When I run the .exe I get an error

```
QWindowsEGLStaticContext::create: Failed to load and resolve libEGL function
Failed to load opengl32sw.dll (The specified module could not be found.)
Failed to load and resolve WGL/OpenGL functions
Failed to create OpenGL context for format QsurfaceFormat(version 2.0, options QFlags<QSurfaceFormat::FormatOption>(), depthBufferSize 24, ...<snip>
This is most likely caused by not having the necessary graphics drivers installed.

Install a driver providing OpenGL 2.0 or higher, or, if this is not possible, make sure the ANGLE Open GL ES 2.0 emulation libraries (libEGL.dll, libLESv2.dll and d3dcompiler_*.dll) are available in the application executabl's directory or in a location listed in PATH.
```

To run the application I can copy these four .dlls into the `dist\main\` directory.

- libEGL.dll
- libGLESv2.dll
- d3dcompiler_47.dll
- opengl32sw.dll

When I run it I get Command Prompt window with this output.
```
QWindowsWGLStaticContext::create: Could not initialize EGL display: error 0x3001
QWindowsWGLStaticContext::create: When using ANGLE, check if d3dcompiler_4x.dll is available.
```
Instead of copying those .dll files I can add the Qt bin directory to my PATH.

```
set PATH=%PATH%;C:\Qt\5.10.1\msvc2015\bin
call main.exe
QWindowsWGLStaticContext::create: Could not initialize EGL display: error 0x3001
QWindowsWGLStaticContext::create: When using ANGLE, check if d3dcompiler_4x.dll is available.
```

When I copy the `dist\main\` to another computer (Windows 10).

I have to set two environment variables before the application will work.

```
set QT_QPA_PLATFORM_PLUGIN_PATH=%exeDir%\PyQt5\Qt\plugins\platforms
set QML2_IMPORT_PATH=%exeDir%\PyQt5\Qt\qml
```

There are no error messages on the Windows 10 computer with these two environment variables set.


Discussion:
@Siecje, thanks for looking at this. Do you mean that setting these two environment vars is all that's needed? That is, either copy the extra DLLs or just set the env var?
If I copy the PyInstaller result from Windows 7 to Windows 10 I only have to set those two environment variables for it to work.
OK, thanks. Per [PyInstaller/loader/rthooks/pyi_rth_qt5.py](https://github.com/pyinstaller/pyinstaller/blob/develop/PyInstaller/loader/rthooks/pyi_rth_qt5.py) `QML2_IMPORT_PATH` should already be set.  Would you mind appending the line:

``` Python
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins', 'platforms')
```

to this file and see if that fixes it?
I tried that and it didn't work. I also tried a custom runtime hook.

It seems that once the application is running it is too late?
Hmm. This code should run before Qt does, so I'm confused. I'm busy now, but will do some debug when I can.
A quick idea: I wonder if PySide is somehow being detected, so the [qml rthook](https://github.com/pyinstaller/pyinstaller/blob/develop/PyInstaller/loader/rthooks/pyi_rth_qml.py) or the [qt plugins rthook](https://github.com/pyinstaller/pyinstaller/blob/develop/PyInstaller/loader/rthooks/pyi_rth_qt5plugins.py) is being used. Would you mind printing `os.environ['QT_QPA_PLATFORM_PLUGIN_PATH']` and `os.environ['QML2_IMPORT_PATH']` from within your frozen application to see what these end up as?
EDIT: I was getting an traceback from `json.loads('')` because this exec_statement was returning an empty string. https://github.com/pyinstaller/pyinstaller/blob/develop/PyInstaller/utils/hooks/qt.py#L57

The reason it was empty was because I couldn't import `from PyQt5.QtCore import QLibraryInfo` due to a missing .dll.

I forgot I need to set

```shell
set QT5DIR=C:\Qt\5.10.1\msvc2015
set PATH=%PATH%;C:\Qt\5.10.1\msvc2015\bin
```

before running PyInstaller.

`os.environ['QT_QPA_PLATFORM_PLUGIN_PATH']` is not set.
`os.environ['QML2_IMPORT_PATH']` has the correct value.

This is on both the Windows 7 build computer and the Windows 10 test computer.

`QT_QPA_PLATFORM_PLUGIN_PATH` is only required for it to work on Windows 10.

I set `QT_QPA_PLATFORM_PLUGIN_PATH` before running the application otherwise I get this error message in the Command Prompt.

```
This application failed to start because it could not find or load the Qt platform plugin "windows"
in "".

Reinstalling the application may fix this problem.
```

`os.environ['QML2_IMPORT_PATH']` has the correct value but I still need to set it before running the application for it to work.

The value of `os.environ['QML2_IMPORT_PATH']` is the same but if I don't set it before running the application I get the familiar QML import errors.

```
file:///C:/Users/Cody/Downloads/main(2)/main/Main.qml:3 module "QtQuick.Layouts" is not installed
file:///C:/Users/Cody/Downloads/main(2)/main/Main.qml:1 module "QtQuick" is not installed
file:///C:/Users/Cody/Downloads/main(2)/main/Main.qml:2 module "QtQuick.Controls" is not installed
file:///C:/Users/Cody/Downloads/main(2)/main/Main.qml:3 module "QtQuick.Layouts" is not installed
file:///C:/Users/Cody/Downloads/main(2)/main/Main.qml:1 module "QtQuick" is not installed
file:///C:/Users/Cody/Downloads/main(2)/main/Main.qml:2 module "QtQuick.Controls" is not installed
file:///C:/Users/Cody/Downloads/main(2)/main/Main.qml:3 module "QtQuick.Layouts" is not installed
file:///C:/Users/Cody/Downloads/main(2)/main/Main.qml:1 module "QtQuick" is not installed
file:///C:/Users/Cody/Downloads/main(2)/main/Main.qml:2 module "QtQuick.Controls" is not installed
```
@Siecje, thanks for the feedback. I'm surprised that the environment variables aren't set by the rthooks. I'll so some investigation on my end to see if I can understand what's going on.
Well `os.environ['QML2_IMPORT_PATH']` is being set. I think it is just being set after the application is launched and it is too late. 
I did some testing on my PC (Windows 10, Python 3.6.1, PyQt5 5.10.1). I made one change to your code in `main.py`, adding `import PyQt5.QtQuick` since that triggers the hook that pulls in the QML files (see https://github.com/pyinstaller/pyinstaller/blob/develop/PyInstaller/hooks/hook-PyQt5.QtQuick.py). Should this code instead be placed in `hook-PyQt5.QtQml`?

After the PyInstaller build, I manually added `copy *.qml dist\main` and `copy *.js dist\main`. When I run that, everything works. Does adding this import change the behavior for you?
I'm sorry I should have been more clear, PyInstaller work on Python 3.6 but not on Python 3.4 on Windows 7 32-bit Windows and Python.

It works for me on Windows 10 with Python 3.6.5 (64-bit) and PyQt5==5.10 (not 5.10.1 I think the 64-bit PyQt5==5.10.1 wheel is missing something.)

In the main.spec file 

https://github.com/Siecje/qml-testing/blob/PyInstaller/main.spec

I already have

```
hiddenimports=[
                  "PyQt5.QtQuick", "PyQt5.QtQml",
             ],
```

And it is picking it up.

```
5310 INFO: Caching module hooks...
5310 INFO: Analyzing main.py
5325 INFO: Loading module hooks...
5325 INFO: Loading module hook "hook-encodings.py"...
5403 INFO: Loading module hook "hook-pydoc.py"...
5403 INFO: Loading module hook "hook-PyQt5.py"...
5450 INFO: Loading module hook "hook-PyQt5.QtCore.py"...
6016 INFO: Loading module hook "hook-PyQt5.QtGui.py"...
6704 INFO: Loading module hook "hook-PyQt5.QtQml.py"...
7321 INFO: Loading module hook "hook-PyQt5.QtQuick.py"...
8893 INFO: Loading module hook "hook-xml.py"...
9190 INFO: Loading module hook "hook-PyQt5.QtNetwork.py"...
9752 INFO: Looking for ctypes DLLs
9752 INFO: Analyzing run-time hooks ...
```

Since this isn't an easy environment for me to reproduce, I won't help with by debugging it myself. However, I'm happy to offer some suggestions; if there's a way to solve this, I'd like to include it in Pyinstaller.

The core issue, from what I understand, is that two environment variables,  `os.environ['QT_QPA_PLATFORM_PLUGIN_PATH']` and `os.environ['QML2_IMPORT_PATH']`, must be set correctly for your application to work. If you set them manually at the command prompt, the application works. Would you therefore try setting these in your Python code before https://github.com/Siecje/qml-testing/blob/PyInstaller/main.py#L6 (the import of PyQt5)?
I have tried that, it doesn't change the behaviour. That is essentially what the runtime hook is doing, right?

Thanks for testing. Yes, that's exactly what the run-time hooks do. This should be *exactly* what setting the variables at the command prompt does, also. Therefore, I'm guessing that somewhere, somehow PyQt5 is being imported (and reading those environment variables) before you set them, perhaps in an unexpected run-time hook. For example, https://github.com/pyinstaller/pyinstaller/blob/develop/PyInstaller/loader/rthooks/pyi_rth_qt5plugins.py (a run-time hook for PySide2) does this.

I'd suggest also looking at the build output from PyInstaller to see if PySide2 or something else is being picked up. Perhaps also pass the `-v` option to the frozen Python interpreter, to see when PyQt5 is imported versus when the environment variables are set? After that, I'm out of ideas; I can't imagine why setting environment variables at the command prompt would differ from setting them in your Python code. Would you mind posting your `main.py` or updating your github repo, so I could check the ordering there?
How can I pass `-v` to the frozen Python interpreter?
Here is the full PyInstaller output

```
(venv) C:\Users\testvm\Desktop\bjones\qml-testing-PyInstaller>pyinstaller main.s
pec
109 INFO: PyInstaller: 3.4.dev0+1033a8770
109 INFO: Python: 3.4.4
109 INFO: Platform: Windows-7-6.1.7601-SP1
125 INFO: UPX is not available.
125 INFO: Extending PYTHONPATH with paths
['C:\\Users\\testvm\\Desktop\\bjones\\qml-testing-PyInstaller',
 'C:\\Users\\testvm\\Desktop\\bjones\\qml-testing-PyInstaller']
125 INFO: checking Analysis
125 INFO: Building Analysis because Analysis-00.toc is non existent
125 INFO: Initializing module dependency graph...
125 INFO: Initializing module graph hooks...
140 INFO: Analyzing base_library.zip ...
4609 INFO: Processing pre-find module path hook   distutils
8828 INFO: Analyzing hidden import 'PyQt5.QtQuick'
8859 INFO: Analyzing hidden import 'PyQt5.QtQml'
8859 INFO: running Analysis Analysis-00.toc
9468 INFO: Caching module hooks...
9484 INFO: Analyzing main.py
9500 INFO: Loading module hooks...
9500 INFO: Loading module hook "hook-PyQt5.py"...
9500 INFO: Loading module hook "hook-encodings.py"...
9640 INFO: Loading module hook "hook-xml.py"...
10250 INFO: Loading module hook "hook-PyQt5.QtQuick.py"...
13328 INFO: Loading module hook "hook-PyQt5.QtCore.py"...
13515 INFO: Loading module hook "hook-PyQt5.QtQml.py"...
13828 INFO: Loading module hook "hook-PyQt5.QtGui.py"...
14218 INFO: Loading module hook "hook-PyQt5.QtNetwork.py"...
14484 INFO: Loading module hook "hook-distutils.py"...
14515 INFO: Loading module hook "hook-pydoc.py"...
14578 INFO: Looking for ctypes DLLs
14609 INFO: Analyzing run-time hooks ...
14609 INFO: Including run-time hook 'pyi_rth_qt5.py'
14625 INFO: Looking for dynamic libraries
98391 INFO: Looking for eggs
98391 INFO: Using Python library C:\Windows\system32\python34.dll
98391 INFO: Found binding redirects:
[]
98407 INFO: Warnings written to C:\Users\testvm\Desktop\bjones\qml-testing-PyIns
taller\build\main\warn-main.txt
98500 INFO: Graph cross-reference written to C:\Users\testvm\Desktop\bjones\qml-
testing-PyInstaller\build\main\xref-main.html
98907 INFO: Appending 'datas' from .spec
98922 INFO: checking PYZ
98922 INFO: Building PYZ because PYZ-00.toc is non existent
98922 INFO: Building PYZ (ZlibArchive) C:\Users\testvm\Desktop\bjones\qml-testin
g-PyInstaller\build\main\PYZ-00.pyz
100172 INFO: Building PYZ (ZlibArchive) C:\Users\testvm\Desktop\bjones\qml-testi
ng-PyInstaller\build\main\PYZ-00.pyz completed successfully.
100204 INFO: checking PKG
100204 INFO: Building PKG because PKG-00.toc is non existent
100204 INFO: Building PKG (CArchive) PKG-00.pkg
100235 INFO: Building PKG (CArchive) PKG-00.pkg completed successfully.
100235 INFO: Bootloader c:\users\testvm\downloads\venv\lib\site-packages\PyInsta
ller\bootloader\Windows-32bit\run.exe
100235 INFO: checking EXE
100235 INFO: Building EXE because EXE-00.toc is non existent
100235 INFO: Building EXE from EXE-00.toc
100235 INFO: Appending archive to EXE C:\Users\testvm\Desktop\bjones\qml-testing
-PyInstaller\build\main\main.exe
100250 INFO: Building EXE from EXE-00.toc completed successfully.
100266 INFO: checking COLLECT
100266 INFO: Building COLLECT because COLLECT-00.toc is non existent
100266 INFO: Building COLLECT COLLECT-00.toc
156079 INFO: Building COLLECT COLLECT-00.toc completed successfully.
```
The build output doesn't mention any PySide2 hooks, so that seems fine.

See https://pyinstaller.readthedocs.io/en/v3.3.1/spec-files.html#giving-run-time-python-options for the `-v` options -- it's an easy `.spec` file tweak.
```
C:\Users\Cody\Downloads\main(3)\main>call main.exe
import _frozen_importlib # frozen
import imp # builtin
import sys # builtin
# installing zipimport hook
# installed zipimport hook
# zipimport: found 148 names in 'C:\\Users\\Cody\\Downloads\\main(3)\\main\\base_library.zip'
import codecs # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\codecs.pyc
import encodings.aliases # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\encodings\aliases.pyc
import encodings # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\encodings\__init__.pyc
import encodings.mbcs # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\encodings\mbcs.pyc
import encodings.utf_8 # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\encodings\utf_8.pyc
import encodings.latin_1 # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\encodings\latin_1.pyc
import _weakrefset # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\_weakrefset.pyc
import abc # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\abc.pyc
import io # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\io.pyc
import encodings.cp850 # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\encodings\cp850.pyc
# PyInstaller: FrozenImporter(C:\Users\Cody\Downloads\main(3)\main\main.exe?260440)
import os # PyInstaller PYZ
import stat # PyInstaller PYZ
import 'stat' # <pyimod03_importers.FrozenImporter object at 0x00E59150>
import ntpath # PyInstaller PYZ
import genericpath # PyInstaller PYZ
import 'genericpath' # <pyimod03_importers.FrozenImporter object at 0x00E59150>
import 'ntpath' # <pyimod03_importers.FrozenImporter object at 0x00E59150>
# _collections_abc not found in PYZ
import _collections_abc # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\_collections_abc.pyc
import 'os' # <pyimod03_importers.FrozenImporter object at 0x00E59150>
import ctypes # PyInstaller PYZ
# _ctypes not found in PYZ
# _bootlocale not found in PYZ
import _bootlocale # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\_bootlocale.pyc
# encodings.cp1252 not found in PYZ
import encodings.cp1252 # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\encodings\cp1252.pyc
# extension module loaded from 'C:\\Users\\Cody\\Downloads\\main(3)\\main\\_ctypes.pyd'
import ctypes._endian # PyInstaller PYZ
import 'ctypes._endian' # <pyimod03_importers.FrozenImporter object at 0x00E59150>
import 'ctypes' # <pyimod03_importers.FrozenImporter object at 0x00E59150>
import threading # PyInstaller PYZ
# traceback not found in PYZ
# linecache not found in PYZ
import tokenize # PyInstaller PYZ
# collections not found in PYZ
# operator not found in PYZ
import operator # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\operator.pyc
# keyword not found in PYZ
import keyword # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\keyword.pyc
# heapq not found in PYZ
import heapq # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\heapq.pyc
# reprlib not found in PYZ
import reprlib # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\reprlib.pyc
import collections # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\collections\__init__.pyc
# re not found in PYZ
# sre_compile not found in PYZ
# sre_parse not found in PYZ
# sre_constants not found in PYZ
import sre_constants # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\sre_constants.pyc
import sre_parse # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\sre_parse.pyc
import sre_compile # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\sre_compile.pyc
# copyreg not found in PYZ
import copyreg # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\copyreg.pyc
import re # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\re.pyc
import token # PyInstaller PYZ
import 'token' # <pyimod03_importers.FrozenImporter object at 0x00E59150>
import 'tokenize' # <pyimod03_importers.FrozenImporter object at 0x00E59150>
import linecache # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\linecache.pyc
import traceback # loaded from Zip C:\Users\Cody\Downloads\main(3)\main\base_library.zip\traceback.pyc
import 'threading' # <pyimod03_importers.FrozenImporter object at 0x00E59150>
import PyQt5 # PyInstaller PYZ
import 'PyQt5' # <pyimod03_importers.FrozenImporter object at 0x00E59150>
# PyQt5.QtCore not found in PYZ
# sip not found in PYZ
# extension module loaded from 'C:\\Users\\Cody\\Downloads\\main(3)\\main\\sip.pyd'
# extension module loaded from 'C:\\Users\\Cody\\Downloads\\main(3)\\main\\PyQt5.QtCore.pyd'
# PyQt5.QtGui not found in PYZ
# extension module loaded from 'C:\\Users\\Cody\\Downloads\\main(3)\\main\\PyQt5.QtGui.pyd'
# PyQt5.QtQml not found in PYZ
# PyQt5.QtNetwork not found in PYZ
# extension module loaded from 'C:\\Users\\Cody\\Downloads\\main(3)\\main\\PyQt5.QtNetwork.pyd'
# extension module loaded from 'C:\\Users\\Cody\\Downloads\\main(3)\\main\\PyQt5.QtQml.pyd'
```

Without the environment variables this is also in the output.
```
This application failed to start because it could not find or load the Qt platform plugin "windows"
in "".

Reinstalling the application may fix this problem.
```

Just for clarity these are the environment variables.
```
set QT_QPA_PLATFORM_PLUGIN_PATH=PyQt5\Qt\plugins\platforms
set QML2_IMPORT_PATH=PyQt5\Qt\qml
```

The output on the build machine which works looks the same with different memory addresses and a paths.

The build machine is Windows 7 32-bit with Python 3.4.4 32 bit.
Would you add print statements to `main.py`, so it would print some diagnostic output ("the environment variables before importing PyQt5 are...", "PyQt5 import complete"), etc.?
They have the correct values in `os.environ` when it works (set outside of the application) and doesn't work.

I will get them for you.
This is after copying the PyInstaller `dist\main\` to Windows 10 computer and running it.
`QT_QPA_PLATFORM_PLUGIN_PATH` is not set and `QML2_IMPORT_PATH` has a good value.

```
C:\Users\Cody\Downloads\main(4)\main>call main.exe
The environment variables before import PyQt5 are:
QT_QPA_PLATFORM_PLUGIN_PATH

QML2_IMPORT_PATH
C:\Users\Cody\Downloads\main(4)\main\PyQt5\Qt\qml
...
This application failed to start because it could not find or load the Qt platform plugin "windows"
in "".

Reinstalling the application may fix this problem.
```

So first I set `QT_QPA_PLATFORM_PLUGIN_PATH`.

```
C:\Users\Cody\Downloads\main(4)\main>set QT_QPA_PLATFORM_PLUGIN_PATH=PyQt5\Qt\plugins\platforms
```

So both environment variables have a good value but it still can't import the QML files.

```
C:\Users\Cody\Downloads\main(4)\main>call main.exe
The environment variables before import PyQt5 are:
QT_QPA_PLATFORM_PLUGIN_PATH
PyQt5\Qt\plugins\platforms
QML2_IMPORT_PATH
C:\Users\Cody\Downloads\main(4)\main\PyQt5\Qt\qml
...
file:///C:/Users/Cody/Downloads/main(4)/main/Main.qml:2 module "QtQuick.Controls" is not installed
file:///C:/Users/Cody/Downloads/main(4)/main/Main.qml:1 module "QtQuick" is not installed
file:///C:/Users/Cody/Downloads/main(4)/main/Main.qml:3 module "QtQuick.Layouts" is not installed
file:///C:/Users/Cody/Downloads/main(4)/main/Main.qml:2 module "QtQuick.Controls" is not installed
file:///C:/Users/Cody/Downloads/main(4)/main/Main.qml:1 module "QtQuick" is not installed
file:///C:/Users/Cody/Downloads/main(4)/main/Main.qml:3 module "QtQuick.Layouts" is not installed
file:///C:/Users/Cody/Downloads/main(4)/main/Main.qml:2 module "QtQuick.Controls" is not installed
file:///C:/Users/Cody/Downloads/main(4)/main/Main.qml:1 module "QtQuick" is not installed
file:///C:/Users/Cody/Downloads/main(4)/main/Main.qml:3 module "QtQuick.Layouts" is not installed
```

And if I set it to the same value that Python showed it will work.

```
C:\Users\Cody\Downloads\main(4)\main>set QML2_IMPORT_PATH=C:\Users\Cody\Downloads\main(4)\main\PyQt5\Qt\qml

C:\Users\Cody\Downloads\main(4)\main>call main.exe
The environment variables before import PyQt5 are:
QT_QPA_PLATFORM_PLUGIN_PATH
PyQt5\Qt\plugins\platforms
QML2_IMPORT_PATH
C:\Users\Cody\Downloads\main(4)\main\PyQt5\Qt\qml
```

I'm at a loss. Last possible idea -- would you post your `main.py`, or update in your github repo, with these print statement included?
```Python
import os
import sys
import threading
import time

print('The environment variables before import PyQt5 are: ')
print('QT_QPA_PLATFORM_PLUGIN_PATH')
print(os.environ.get('QT_QPA_PLATFORM_PLUGIN_PATH', ''))
print('QML2_IMPORT_PATH')
print(os.environ.get('QML2_IMPORT_PATH', ''))

from PyQt5 import QtCore, QtGui, QtQml
```
Well, I'm lost. I have no idea how to fix this one, and why setting vars in the command prompt differs from setting them using `os.environ`.
Is `os.environ` a copy of the environment but only for Python? Was this the case for Python 3.4.4?

Is it worth building Python 3.4.8 from source?
Changes to `os.environ` should actually modify the environment and vice versa. ???
Any idea why `QT_QPA_PLATFORM_PLUGIN_PATH` is not set? Or why it is required? 

It works on the build machine without being set.

For completness in order for the application to work on the Windows 7 Python 3.4.4 computer I have to copy these 4 files from the Qt bin directory to `dist\main\`.

- libEGL.dll
- libGLESv2.dll
- d3dcompiler_47.dll
- opengl32sw.dll


I have no idea, unfortunately. I don't use QML in the applications I develop. I'm not sure why these binaries aren't being picked up; perhaps they're not in your path when Pyinstaller is doing its build?
They are for the software OpenGL renderer.

Is `QT_QPA_PLATFORM_PLUGIN_PATH` set in one of the hooks?

No -- I assume it should be? (Not that it seems to help your case...sigh....)

If so, is the correct value for this `os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins', 'platforms')`? Do you have any way to testing that this is correct?
Actually if I set `QT_PLUGIN_PATH` then it also works. Which is being done here. 

https://github.com/pyinstaller/pyinstaller/blob/6b2fbcc4d7da7e160cc3cea435b74e15970d253f/PyInstaller/loader/rthooks/pyi_rth_qt5.py

So really my problem is that when I run my executable on Windows 10, `os.environ` is not setting environment variables correctly.
For this, I don't know enough to help. I'd suspect something strange in the bootloader, but can't say much else.
I think the issue of `os.environ` not working might be because I used Visual Studio 2015 to build PyQt5 and Python 3.4.4 was built using Visual Studio 2010.

PyInstaller not including the four .dll files is a problem. They are only required when you need to do software rendering for OpenGL. 

The guess at `os.environ` strangeness sounds reasonable. If/when you rebuild, let me know if that fixes things.

Re: missing `.dll` files, when should these be included? Only with QML applications? Or are there other components of Qt that use OpenGL? I see [mentions](http://doc.qt.io/qt-5/qtgui-index.html#opengl-and-opengl-es-integration) that it's possible in Qt GUI, but sounds difficult.
Whichever hook file it goes in, my guess at the code would be:

``` Python
import os
from glob import glob
from PyInstaller.utils.hooks import pyqt5_library_info

binaries = []
for dll in ('libEGL.dll', 'libGLESv2.dll', 'd3dcompiler_??.dll', 'opengl32sw.dll'):
    dll_path = os.path.join(pyqt5_library_info.location['BinariesPath'], dll)
    # Only add files if they exist.
    if glob(dll_path):
        binaries += [(dll_path, os.path.join('PyQt5', 'Qt', 'bin', dll)]
```

Would you mind testing this out (for now, put it in `hook-PyQt5.py`) to see if this helps?
Wow thanks for all your help!

What about `opengl32sw.dll`?

These files are only used a fallback if you don't have a display driver that supports OpenGL >= 2.0.

Another issue is that the PyQt5 wheel doesn't currently include `opengl32sw.dll` or `d3dcompiler_47.dll`[0]. But they are available in the Qt bin directory, if you install Qt.

0: https://www.riverbankcomputing.com/pipermail/pyqt/2018-June/040378.html
Oops! I updated the code. I'll go ahead and put thing in `hook-PyQt5.py`, unless you think it belongs elsewhere. (That is, include this for all PyQt5 cases, not just for QML only or QtGui only.)
I have a Widgets application that doesn't require those files to run on the same minimal system, I'll try to find out when those DLLs required.

For the code, `dll` and `dll_path` will still contain `??` for `'d3dcompiler_??.dll'`. I think you want to add the correct file name to binaries.

```Python
if glob(dll_path):
    binaries += [(dll_file_path, os.path.join('PyQt5', 'Qt', 'bin', os.path.basename(dll_file_path))) for dll_file_path in glob(dll_path)]
```
I see this has been commited now. https://github.com/pyinstaller/pyinstaller/commit/58aa0d5ded765a004dac89b456906d45152c060f

Does the commented code actually cause errors if the files are not available? The glob should just return an empty list.

Also should the list comprehension be set to `binaries`?

I still think the proper path should be used as mentioned in my previous comment.

@Siecje, thanks for the feedback. 

> I see this has been commited now. https://github.com/pyinstaller/pyinstaller/commit/58aa0d5ded765a004dac89b456906d45152c060f

I'm trying to get all the new PyQt5 support finalized while I have time...

> Does the commented code actually cause errors if the files are not available? The glob should just return an empty list.

Yes, it does. Appveyor complained about the missing `d3dcompiler_47.dll` on execution; it's not a missing file/glob being treated as a filename error. Feel free to submit a fake PR with code uncommented to see the Appveyor output. Just close it, so we don't waste time on debug.

> Also should the list comprehension be set to `binaries`?

I'm confused -- the results are being assigned to `binaries` already. ???

> I still think the proper path should be used as mentioned in my previous comment.

PyInstaller handles globs -- my code just tests that the files exist, rather than un-globbing them. If a non-globbed file doesn't exist, PyInstaller will terminate with an error, which is what I'm trying to avoid.
> I'm confused -- the results are being assigned to binaries already. ???

I'm talking about this line. https://github.com/pyinstaller/pyinstaller/commit/58aa0d5ded765a004dac89b456906d45152c060f#diff-3fbf07e96767283188b66d3512b56894R23

### d3dcompiler_47.dll

I believe the error is caused by a bad tuple being added to binaries.

`binaries` will contain:

```
[('/path/to/site-packages/PyQt5/Qt/bin/d3dcompiler_??.dll', 'PyQt5/Qt/bin/d3dcompiler_??.dll')]
```

With my code in the above comment it will contain:

```
[('/path/to/site-packages/PyQt5/Qt/bin/d3dcompiler_47.dll', 'PyQt5/Qt/bin/d3dcompiler_47.dll')]
```

I'm not 100% sure this is the problem because you said the error message contained the 47 for d3dcompiler_47.dll.

Resolving patch:
diff --git a/PyInstaller/hooks/hook-PyQt5.py b/PyInstaller/hooks/hook-PyQt5.py
--- a/PyInstaller/hooks/hook-PyQt5.py
+++ b/PyInstaller/hooks/hook-PyQt5.py
@@ -6,6 +6,7 @@
 #
 # The full license is in the file COPYING.txt, distributed with this software.
 #-----------------------------------------------------------------------------
+import glob
 import os
 
 from PyInstaller.utils.hooks import pyqt5_library_info, collect_system_data_files
@@ -18,16 +19,42 @@
                                    'PyQt5')
          if os.path.basename(x[0]) == 'qt.conf']
 
-# Include ICU files, if they exist. See the "Deployment approach" section in
-# ``PyInstaller/utils/hooks/qt.py``.
-[(os.path.join(pyqt5_library_info.location['BinariesPath'], dll),
-  os.path.join('PyQt5', 'Qt', 'bin', dll))
- for dll in ('icudt??.dll', 'icuin??.dll', 'icuuc??.dll')]
-
-# TODO: Include software rendering for OpenGL. See the "Deployment approach". However, because the standard PyQt5 wheel `doesn't include <https://www.riverbankcomputing.com/pipermail/pyqt/2018-June/040387.html>`_ ``d3dcompiler_XX.dll``, this produces failures. When the wheel is updated, this code can be uncommented.
-##binaries = []
-##for dll in ('libEGL.dll', 'libGLESv2.dll', 'd3dcompiler_??.dll', 'opengl32sw.dll'):
-##    dll_path = os.path.join(pyqt5_library_info.location['BinariesPath'], dll)
-##    # Only add files if they exist.
-##    if glob(dll_path):
-##        binaries += [(dll_path, os.path.join('PyQt5', 'Qt', 'bin', dll))]
+
+def find_all_or_none(globs_to_include, num_files):
+    """
+    globs_to_include is a list of file name globs
+    If the number of found files does not match num_files
+    then no files will be included.
+    """
+    # TODO: This function is required because CI is failing to include libEGL
+    # The error in AppVeyor is:
+    # [2312] LOADER: Running pyi_lib_PyQt5-uic.py
+    # Failed to load libEGL (Access is denied.)
+    # More info: https://github.com/pyinstaller/pyinstaller/pull/3568
+    # Since the PyQt5 wheels do not include d3dcompiler_4?.dll, libEGL.dll and
+    # libGLESv2.dll will not be included for PyQt5 builds during CI.
+    to_include = []
+    for dll in globs_to_include:
+        dll_path = os.path.join(pyqt5_library_info.location['BinariesPath'],
+                                dll)
+        dll_file_paths = glob.glob(dll_path)
+        for dll_file_path in dll_file_paths:
+            file_name = os.path.basename(dll_file_path)
+            dst_dll_path = os.path.join('PyQt5', 'Qt', 'bin', file_name)
+            to_include.append((dll_file_path, dst_dll_path))
+    if len(to_include) == num_files:
+        return to_include
+    return []
+
+
+binaries = []
+angle_files = ['libEGL.dll', 'libGLESv2.dll', 'd3dcompiler_??.dll']
+binaries += find_all_or_none(angle_files, 3)
+
+opengl_software_renderer = ['opengl32sw.dll']
+binaries += find_all_or_none(opengl_software_renderer, 1)
+
+# Include ICU files, if they exist.
+# See the "Deployment approach" section in ``PyInstaller/utils/hooks/qt.py``.
+icu_files = ['icudt??.dll', 'icuin??.dll', 'icuuc??.dll']
+binaries += find_all_or_none(icu_files, 3)


Test patch: