Repository: napari/napari

Issue:
Icons not loading with napari 0.3.0
## 🐛 Bug

When trying to start napari, the interface loads, but all icons are absent.
![grafik](https://user-images.githubusercontent.com/3041236/81206152-f92f9680-8fcb-11ea-8c24-281aeabcda21.png)

## To Reproduce

Steps to reproduce the behavior:

`python -m napari`

In the console
```
WARNING: DirectWrite: CreateFontFaceFromHDC() failed (Indicates an error in an input file such as a font file.) for QFontDef(Family="8514oem", pointsize=12, pixelsize=20, styleHint=5, weight=50, stretch=100, hintingPreference=0) LOGFONT("8514oem", lfWidth=0, lfHeight=-20) dpi=144
WARNING: Cannot open file ':/themes/dark/right_arrow.svg', because: No such file or directory
WARNING: Cannot open file ':/themes/dark/right_arrow.svg', because: No such file or directory
WARNING: Cannot open file ':/themes/dark/delete_shape.svg', because: No such file or directory
... and so on for all the other icon files
```

## Expected behavior

The icons should load
## Environment
```
napari: 0.3.0
Platform: Windows-10-10.0.18362-SP0
Python: 3.7.6 | packaged by conda-forge | (default, Mar 23 2020, 22:22:21) [MSC v.1916 64 bit (AMD64)]
Qt: 5.12.5
PyQt5: 5.12.3
NumPy: 1.18.1
SciPy: 1.3.1
Dask: 2.13.0
VisPy: 0.6.4

GL version: 4.6.0 NVIDIA 441.22
MAX_TEXTURE_SIZE: 32768

Plugins:
- napari-plugin-engine: 0.1.4
- svg: 0.1.2
```
I got the same issue on another Windows installation, but not on OS X (where I also have an older QT version than officially required by napari)

Need consensus on options for delivering icons & resources
## ❓ Question

As shown in #1209, it looks like the strategy of building icons after installation is not going to work in every case.  Even though I tried to put in three levels of "fallback" behavior, in case of permissions errors and stuff, I think it's probably safer to ship a default resources file that is checked into source (or built just prior to uploading our package to PyPI)... 

This brings us back to cross pyside/pyqt resource compatibility and compatibility of resources built with various rcc versions.  I just tested a crapload of combinations of `_qt_resources.py` files built with various versions of `pyside2-rcc` or `pyrcc5`, and tested them on various installs of napari using pyside2 and qt5 installed with both pip and conda.

## tldr;
resources built with the `pyrcc5` binary from `pyqt5` have much broader compatibility than those built with `pyside2-rcc`.  So if we pre-build using the latest `pyrcc5`, we should be able to expect it to be supported on all version of pyside2 and pyqt5 available on either pip or conda.  (that is not the case with `pyside2-rcc`)

<details>
<summary>exact combinations I tested</summary>

## resources generated with pyside2-rcc v5.14.2  (from pip)

### using pip to install pyside2/pyqt5

>PySide2==5.11.2 -> 'Shiboken.ObjectType' object is not iterable
>PySide2==5.12.6 -> 'Shiboken.ObjectType' object is not iterable
>PySide2==5.13.2 -> 'Shiboken.ObjectType' object is not iterable

PySide2==5.14.0 -> **works**
PySide2==5.14.2.1 -> **works**

(PyQt5 < 5.11.3 -> uninstallable on pip due to sip)
PyQt5==5.11.3 -> no icons
PyQt5==5.12.3 -> no icons
PyQt5==5.13.2 -> **works**
PyQt5==5.14.2 -> **works**

### using conda to install pyside2/pyqt5

pyside2=5.12.4.py37h9cca949_0      conda-forge -> no icons
pyside2=5.13.2.py37ha8f7116_2     conda-forge -> no icons
pyqt=5.9.2.py37h2a560b1_4      conda-forge -> no icons
pyqt=5.12.3.py37ha62fc16_3      conda-forge -> no icons

## resources generated with pyrcc5 v5.14.2  (from pip)

### using pip to install pyside2/pyqt5

PySide2==5.14.2.1 -> **works**
PyQt5==5.11.3 -> **works**
PyQt5==5.12.3 -> **works**
PyQt5==5.13.2 -> **works**
PyQt5==5.14.2 -> **works**

### using conda to install pyside2/pyqt5

pyside2=5.12.4.py37h9cca949_0      conda-forge -> **works**
pyside2=5.13.2.py37ha8f7116_2      conda-forge -> **works**
pyqt=5.9.2.py37h2a560b1_4      conda-forge -> **works**
pyqt=5.12.3.py37ha62fc16_3      conda-forge -> **works**

</details>

### question for @napari/core-devs 
I think we should bundle resources built with `pyrcc5`, rather than building them after install. (though end-users could still be able to rebuild icons using `python -m napari.resources.build_icons`).  How best to do this?
- Just have someone manually build it and check it into source whenever it needs updating?  making sure they are building with `pyrcc5` instead of `pyside2-rcc` (testing for this is possible)... 
- not check into source, and have it built on github actions prior to deployment, by installing `pyqt5` prior to running `setup.py`. This is made complicated by the fact that we actually check to see which backend is installed when writing our requirements.  So we'd basically have to pip install pyqt5, build the icons, uninstall, pip install pyside2, run setup.py... 🙄


Discussion:
bummer!  sorry about that!
If you create a fresh environment, do you get the same problem?  (trying to figure out if we have a general write permissions issue on your setup, or an issue with the binary that generates the icons)
I can confirm this happens in a fresh environment as well.

```
napari: 0.3.0
Platform: Windows-10-10.0.18362-SP0
Python: 3.7.7 (default, May 6 2020, 11:45:54) [MSC v.1916 64 bit (AMD64)]
Qt: 5.12.5
PyQt5: 5.12.3
NumPy: 1.18.1
SciPy: 1.4.1
Dask: 2.15.0
VisPy: 0.6.4

GL version: 4.6.0 NVIDIA 441.22
MAX_TEXTURE_SIZE: 32768

Plugins:
- napari-plugin-engine: 0.1.4
- svg: 0.1.2
```
thanks for checking!  I definitely have some thoughts for how to avoid this... regardless of what the specific cause is in this case and I'll start working on that.  but just in the interest of trying to figure that out, can you tell me (in code) how you created an environment and how you installed napari?

was it:
```
conda create -n new-env -y python=3.7
conda activate new-env
pip install napari
``` 

or did you install with a different method and/or environment setup?
It was like this, except I installed qt, pyqt, numpy and scipy with conda from conda-forge
On Windows I used to experience problems with pip versions of these packages so I install them via conda out of habit (also the conda numpy is build with MKL by default).
ok thanks!
can you test one more thing for me?  run:
`python -c "import napari; print(napari.resources.import_resources())"`
and tell me where it's trying to write the file and whether there are any errors?
if that file exists, try deleting it and running it again (and paste the full error if you get one)...

a little more backstory here: it turned out that icon resources built with some versions of pyside/pyqt don't always work for other versions... so we moved away from shipping a single prebuilt icon file to a build-on-site strategy, but I'm assuming it's unable to write that file for some reason on your computer.  we can go back to shipping the pre-built resources if necessary, but we'll need to do some testing to explore compatibility of various qt APIs and possibly ship multiple files.
@tlambert03 I get
`C:\Users\vilim\miniconda3\envs\rplab\lib\site-packages\napari\resources\_qt_resources_0.3.0_pyqt5_5.12.5.py`
There are no errors. Deleting the file and running napari or `import_resources()` generates the file and there are no errors again, but no icons appear.
thanks again!  two more requests when you get a moment:
1) could you try replacing that `_qt_resources` file with [this file](https://github.com/napari/napari/files/4598677/_qt_resources_0.3.0_pyqt5_5.12.5.txt) (you'll need to change the extension from .txt to `.py`) and see if it works
2) paste the full output of `conda list` here


1) works, after replacing the file the icons appear
2) here it is:

<details>
<summary> conda list output </summary>

```
# packages in environment at C:\Users\vilim\miniconda3\envs\testnapari:
#
# Name                    Version                   Build  Channel
alabaster                 0.7.12                   pypi_0    pypi
babel                     2.8.0                    pypi_0    pypi
backcall                  0.1.0                    pypi_0    pypi
blas                      1.0                         mkl
ca-certificates           2020.4.5.1           hecc5488_0    conda-forge
cachey                    0.2.1                    pypi_0    pypi
certifi                   2020.4.5.1       py37hc8dfbb8_0    conda-forge
chardet                   3.0.4                    pypi_0    pypi
colorama                  0.4.3                    pypi_0    pypi
dask                      2.15.0                   pypi_0    pypi
decorator                 4.4.2                    pypi_0    pypi
docutils                  0.16                     pypi_0    pypi
freetype                  2.10.1               ha9979f8_0    conda-forge
freetype-py               2.1.0.post1                py_0    conda-forge
fsspec                    0.7.3                    pypi_0    pypi
heapdict                  1.0.1                    pypi_0    pypi
icc_rt                    2019.0.0             h0cc432a_1
icu                       64.2                 he025d50_1    conda-forge
idna                      2.9                      pypi_0    pypi
imagecodecs               2020.2.18                pypi_0    pypi
imageio                   2.8.0                    pypi_0    pypi
imagesize                 1.2.0                    pypi_0    pypi
importlib-metadata        1.6.0                    pypi_0    pypi
intel-openmp              2020.0                      166
ipykernel                 5.2.1                    pypi_0    pypi
ipython                   7.14.0                   pypi_0    pypi
ipython-genutils          0.2.0                    pypi_0    pypi
jedi                      0.17.0                   pypi_0    pypi
jinja2                    2.11.2                   pypi_0    pypi
jpeg                      9c                hfa6e2cd_1001    conda-forge
jupyter-client            5.3.4                    pypi_0    pypi
jupyter-core              4.6.3                    pypi_0    pypi
libclang                  9.0.1           default_hf44288c_0    conda-forge
libpng                    1.6.37               hfe6a214_1    conda-forge
markupsafe                1.1.1                    pypi_0    pypi
mkl                       2020.0                      166
mkl-service               2.3.0            py37hb782905_0
mkl_fft                   1.0.15           py37h14836fe_0
mkl_random                1.1.0            py37h675688f_0
napari                    0.3.0                    pypi_0    pypi
napari-plugin-engine      0.1.4                    pypi_0    pypi
napari-svg                0.1.2                    pypi_0    pypi
numpy                     1.18.1           py37h93ca92e_0
numpy-base                1.18.1           py37hc3f5095_1
numpydoc                  0.9.2                    pypi_0    pypi
openssl                   1.1.1g               he774522_0    conda-forge
packaging                 20.3                     pypi_0    pypi
parso                     0.7.0                    pypi_0    pypi
pickleshare               0.7.5                    pypi_0    pypi
pillow                    7.1.2                    pypi_0    pypi
pip                       20.0.2                   py37_1
prompt-toolkit            3.0.5                    pypi_0    pypi
psutil                    5.7.0                    pypi_0    pypi
pygments                  2.6.1                    pypi_0    pypi
pyopengl                  3.1.5                    pypi_0    pypi
pyparsing                 2.4.7                    pypi_0    pypi
pyqt                      5.12.3           py37h1834ac0_3    conda-forge
pyqt5-sip                 4.19.18                  pypi_0    pypi
pyqtchart                 5.12                     pypi_0    pypi
pyqtwebengine             5.12.1                   pypi_0    pypi
pyside2                   5.14.2.1                 pypi_0    pypi
python                    3.7.7                h81c818b_4
python-dateutil           2.8.1                    pypi_0    pypi
python_abi                3.7                     1_cp37m    conda-forge
pytz                      2020.1                   pypi_0    pypi
pywin32                   227                      pypi_0    pypi
pyzmq                     19.0.0                   pypi_0    pypi
qt                        5.12.5               h7ef1ec2_0    conda-forge
qtconsole                 4.7.3                    pypi_0    pypi
qtpy                      1.9.0                    pypi_0    pypi
requests                  2.23.0                   pypi_0    pypi
scipy                     1.4.1            py37h9439919_0
setuptools                46.1.3                   py37_0
shiboken2                 5.14.2.1                 pypi_0    pypi
six                       1.14.0                   py37_0
snowballstemmer           2.0.0                    pypi_0    pypi
sphinx                    3.0.3                    pypi_0    pypi
sphinxcontrib-applehelp   1.0.2                    pypi_0    pypi
sphinxcontrib-devhelp     1.0.2                    pypi_0    pypi
sphinxcontrib-htmlhelp    1.0.3                    pypi_0    pypi
sphinxcontrib-jsmath      1.0.1                    pypi_0    pypi
sphinxcontrib-qthelp      1.0.3                    pypi_0    pypi
sphinxcontrib-serializinghtml 1.1.4                    pypi_0    pypi
sqlite                    3.31.1               h2a8f88b_1
tifffile                  2020.5.5                 pypi_0    pypi
toolz                     0.10.0                   pypi_0    pypi
tornado                   6.0.4                    pypi_0    pypi
traitlets                 4.3.3                    pypi_0    pypi
urllib3                   1.25.9                   pypi_0    pypi
vc                        14.1                 h0510ff6_4
vispy                     0.6.4            py37hc8d92b1_0    conda-forge
vs2015_runtime            14.16.27012          hf0eaf9b_1
wcwidth                   0.1.9                    pypi_0    pypi
wheel                     0.34.2                   py37_0
wincertstore              0.2                      py37_0
wrapt                     1.12.1                   pypi_0    pypi
zipp                      3.1.0                    pypi_0    pypi
zlib                      1.2.11               h62dcd97_4
```
</details>
thanks again for your help!  will try to replicate locally, then get a fix out asap
well this turned out to be a very interesting combination of being on windows, pre-installing pyqt5 with conda, and a change introduced in #1059 in the way that we look for pyqt vs pyside in the environment.  I'll go into greater detail in a PR to fix it ... but can you double check this fix for me on your system just to make sure?
- delete that `napari\resources\_qt_resources_0.3.0_pyqt5_5.12.5.py` file again
- in `napari\resources\build_icons.py` around line 170, change `check_call(['pyrcc5', '-o', out_path, qrc_path])`  to `check_call(['pyrcc5.bat', '-o', out_path, qrc_path])`
- restart napari
Indeed, adding this .🦇 fixed the problem!
Thanks!  will be fixed in next release.
Just to recapitulate some discussion for the meeting: we decided to dig a bit deeper into why autogeneration would fail, rather than go to bundling straight away. If we're still stuck in 1-2 weeks we can revisit the bundling idea.

Resolving patch:
diff --git a/napari/resources/build_icons.py b/napari/resources/build_icons.py
--- a/napari/resources/build_icons.py
+++ b/napari/resources/build_icons.py
@@ -6,7 +6,7 @@
 import os
 import re
 import shutil
-from subprocess import check_call, CalledProcessError
+from subprocess import check_call
 from typing import Dict, List, Tuple
 
 from ..utils.theme import palettes as _palettes
@@ -167,10 +167,24 @@ def build_pyqt_resources(out_path: str, overwrite: bool = False) -> str:
     )
 
     # then convert it to a python file
-    try:
-        check_call(['pyrcc5', '-o', out_path, qrc_path])
-    except (FileNotFoundError, CalledProcessError):
-        check_call(['pyside2-rcc', '-o', out_path, qrc_path])
+    # this looks a little funny, but rather than pick the exact binary to try
+    # once, we use this try/catch loop because
+    # 1. we always want to use pyrcc5 if it's available, regardless of API
+    # 2. it will sometimes, (if not always) be named pyrcc5.bat on windows...
+    # 3. only then do we try pyside2-rcc
+    # see https://github.com/napari/napari/issues/1221 for background
+    for binary in ('pyrcc5.bat', 'pyrcc5', 'pyside2-rcc'):
+        try:
+            check_call([binary, '-o', out_path, qrc_path])
+        except FileNotFoundError:
+            continue
+        break
+    else:
+        raise FileNotFoundError(
+            "Unable to find an executable to build Qt resources (icons).\n"
+            "Tried: 'pyrcc5.bat', 'pyrcc5', 'pyside2-rcc'.\n"
+            "Please open issue at https://github.com/napari/napari/issues/."
+        )
 
     # make sure we import from qtpy
     with open(out_path, "rt") as fin:


Test patch: