We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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... 🙄

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `napari/resources/build_icons.py`
Content:
```
1 """Utility script to generate copies of icons with colors based
2 on our themes. Neccessary workaround because qt does not allow
3 for styling svg elements using qss
4 """
5 
6 import os
7 import re
8 import shutil
9 from subprocess import check_call, CalledProcessError
10 from typing import Dict, List, Tuple
11 
12 from ..utils.theme import palettes as _palettes
13 
14 RESOURCES_DIR = os.path.abspath(os.path.dirname(__file__))
15 SVGPATH = os.path.join(RESOURCES_DIR, 'icons')
16 
17 svg_tag_open = re.compile(r'(<svg[^>]*>)')
18 
19 
20 def themify_icons(
21     dest_dir: str,
22     svg_path: str = SVGPATH,
23     palettes: Dict[str, Dict[str, str]] = _palettes,
24     color_lookup: Dict[str, str] = None,
25 ) -> List[str]:
26     """Create a new "themed" SVG file, for every SVG file in ``svg_path``.
27 
28     Parameters
29     ----------
30     dest_dir : str
31         The directory in which to write all of the themed icons.  Individual
32         themes will be in subdirectories named after each theme.
33     svg_path : str, optional
34         The folder to look in for SVG files, by default will search in a folder
35         named ``icons`` in the same directory as this file.
36     palettes : dict, optional
37         A mapping of ``theme_name: theme_dict``, where ``theme_dict`` is a
38         mapping of color classes to rgb strings. By default will uses palettes
39         from :const:`napari.resources.utils.theme.palettes`.
40     color_lookup : dict, optional
41         A mapping of icon name to color class.  If the icon name is not in the
42         color_lookup, it's color class will be ``"icon"``.
43 
44     Returns
45     -------
46     files : list of str
47         a list of generaged SVG filepaths (each relative to dest_dir)
48     """
49 
50     color_lookup = color_lookup or {
51         'visibility': 'text',
52         'visibility_off': 'highlight',
53         'menu': 'highlight',
54         'drop_down': 'secondary',
55         'plus': 'secondary',
56         'minus': 'secondary',
57         'properties_contract': 'secondary',
58         'properties_expand': 'secondary',
59     }
60     icon_names = [
61         i.replace('.svg', '')
62         for i in os.listdir(SVGPATH)
63         if i.endswith('.svg')
64     ]
65 
66     svg_style_insert = r"""<style type="text/css">
67     path{fill:{{ color }}}
68     polygon{fill:{{ color }}}
69     circle{fill:{{ color }}}
70     rect{fill:{{ color }}}
71     </style>"""
72 
73     files = []
74     for theme_name, palette in palettes.items():
75         palette_dir = os.path.join(dest_dir, theme_name)
76         os.makedirs(palette_dir, exist_ok=True)
77         for icon_name in icon_names:
78             svg_name = icon_name + '.svg'
79             new_file = os.path.join(palette_dir, svg_name)
80             color = color_lookup.get(icon_name, 'icon')
81             css = svg_style_insert.replace('{{ color }}', palette[color])
82             with open(os.path.join(SVGPATH, svg_name), 'r') as fr:
83                 contents = fr.read()
84             with open(new_file, 'w') as fw:
85                 # use regex to find the svg tag and insert css right after
86                 # (the '\\1' syntax includes the matched tag in the output)
87                 fw.write(svg_tag_open.sub(f'\\1{css}', contents))
88             files.append(os.path.join(theme_name, svg_name))
89     return files
90 
91 
92 def build_resources_qrc(
93     dest_dir: str, overwrite: bool = False
94 ) -> Tuple[str, str]:
95     """Create a res.qrc file for all icons generated by ``themify_icons``.
96 
97     Parameters
98     ----------
99     dest_dir : str
100         The directory in which to write create the output file.  Themed SVG
101         icons will also be written to ``dest_dir/themes``.
102     overwrite : bool
103         Whether to force rebuilding of the icons and res.qrc file, by default
104         False
105 
106     Returns
107     -------
108     tuple
109         2-tuple of (path-to-qrc.res, path-to-theme-directory).
110     """
111     qrc_path = os.path.join(dest_dir, 'res.qrc')
112     theme_dir = os.path.join(dest_dir, 'themes')
113     if os.path.exists(qrc_path) and (not overwrite):
114         return qrc_path, theme_dir
115 
116     qrc_string = """
117     <!DOCTYPE RCC>
118     <RCC version="1.0">
119     <qresource>
120     """
121     for filename in themify_icons(theme_dir):
122         qrc_string += f'\n    <file>themes/{filename}</file>'
123 
124     qrc_string += """
125     </qresource>
126     </RCC>
127     """
128 
129     with open(qrc_path, 'w') as f:
130         f.write(qrc_string)
131 
132     return qrc_path, theme_dir
133 
134 
135 def build_pyqt_resources(out_path: str, overwrite: bool = False) -> str:
136     """Build a res.qrc file from icons and convert for python usage.
137 
138     calls :func:`build_resources` and then converts using ``pyside2-rcc`` or
139     ``pyrcc5`` depending on which is installed in the environment.
140     Finally, cleans up autogenerated icon.svgs and res.qrc file after _qt.py
141     file is generated
142 
143     References
144     ----------
145     https://doc.qt.io/qt-5/resources.html
146 
147     Parameters
148     ----------
149     out_path : str
150         Path to write the python resource file.
151     overwrite : bool, optional
152         Whether to force rebuilding of the output file, by default False
153 
154     Returns
155     -------
156     out_path : str
157         Path to the python resource file.  Import this file to make the SVGs
158         and other resources available to Qt stylesheets.
159     """
160 
161     if os.path.exists(out_path) and not overwrite:
162         return out_path
163 
164     # build the resource file to the same path
165     qrc_path, theme_dir = build_resources_qrc(
166         os.path.dirname(out_path), overwrite=overwrite
167     )
168 
169     # then convert it to a python file
170     try:
171         check_call(['pyrcc5', '-o', out_path, qrc_path])
172     except (FileNotFoundError, CalledProcessError):
173         check_call(['pyside2-rcc', '-o', out_path, qrc_path])
174 
175     # make sure we import from qtpy
176     with open(out_path, "rt") as fin:
177         data = fin.read()
178         data = data.replace('PySide2', 'qtpy').replace('PyQt5', 'qtpy')
179     with open(out_path, "wt") as fin:
180         fin.write(data)
181 
182     # cleanup.
183     # we do this here because pip uninstall napari would not collect these
184     # and the final `out_path.py` contains all the necessary bytes info
185     shutil.rmtree(theme_dir, ignore_errors=True)
186     try:
187         os.remove(qrc_path)
188     except Exception:
189         pass
190     return out_path
191
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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 @@
     )
 
     # 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:

{"golden_diff": "diff --git a/napari/resources/build_icons.py b/napari/resources/build_icons.py\n--- a/napari/resources/build_icons.py\n+++ b/napari/resources/build_icons.py\n@@ -6,7 +6,7 @@\n import os\n import re\n import shutil\n-from subprocess import check_call, CalledProcessError\n+from subprocess import check_call\n from typing import Dict, List, Tuple\n \n from ..utils.theme import palettes as _palettes\n@@ -167,10 +167,24 @@\n     )\n \n     # then convert it to a python file\n-    try:\n-        check_call(['pyrcc5', '-o', out_path, qrc_path])\n-    except (FileNotFoundError, CalledProcessError):\n-        check_call(['pyside2-rcc', '-o', out_path, qrc_path])\n+    # this looks a little funny, but rather than pick the exact binary to try\n+    # once, we use this try/catch loop because\n+    # 1. we always want to use pyrcc5 if it's available, regardless of API\n+    # 2. it will sometimes, (if not always) be named pyrcc5.bat on windows...\n+    # 3. only then do we try pyside2-rcc\n+    # see https://github.com/napari/napari/issues/1221 for background\n+    for binary in ('pyrcc5.bat', 'pyrcc5', 'pyside2-rcc'):\n+        try:\n+            check_call([binary, '-o', out_path, qrc_path])\n+        except FileNotFoundError:\n+            continue\n+        break\n+    else:\n+        raise FileNotFoundError(\n+            \"Unable to find an executable to build Qt resources (icons).\\n\"\n+            \"Tried: 'pyrcc5.bat', 'pyrcc5', 'pyside2-rcc'.\\n\"\n+            \"Please open issue at https://github.com/napari/napari/issues/.\"\n+        )\n \n     # make sure we import from qtpy\n     with open(out_path, \"rt\") as fin:\n", "issue": "Icons not loading with napari 0.3.0\n## \ud83d\udc1b Bug\r\n\r\nWhen trying to start napari, the interface loads, but all icons are absent.\r\n![grafik](https://user-images.githubusercontent.com/3041236/81206152-f92f9680-8fcb-11ea-8c24-281aeabcda21.png)\r\n\r\n## To Reproduce\r\n\r\nSteps to reproduce the behavior:\r\n\r\n`python -m napari`\r\n\r\nIn the console\r\n```\r\nWARNING: 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\r\nWARNING: Cannot open file ':/themes/dark/right_arrow.svg', because: No such file or directory\r\nWARNING: Cannot open file ':/themes/dark/right_arrow.svg', because: No such file or directory\r\nWARNING: Cannot open file ':/themes/dark/delete_shape.svg', because: No such file or directory\r\n... and so on for all the other icon files\r\n```\r\n\r\n## Expected behavior\r\n\r\nThe icons should load\r\n## Environment\r\n```\r\nnapari: 0.3.0\r\nPlatform: Windows-10-10.0.18362-SP0\r\nPython: 3.7.6 | packaged by conda-forge | (default, Mar 23 2020, 22:22:21) [MSC v.1916 64 bit (AMD64)]\r\nQt: 5.12.5\r\nPyQt5: 5.12.3\r\nNumPy: 1.18.1\r\nSciPy: 1.3.1\r\nDask: 2.13.0\r\nVisPy: 0.6.4\r\n\r\nGL version: 4.6.0 NVIDIA 441.22\r\nMAX_TEXTURE_SIZE: 32768\r\n\r\nPlugins:\r\n- napari-plugin-engine: 0.1.4\r\n- svg: 0.1.2\r\n```\r\nI 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)\r\n\nNeed consensus on options for delivering icons & resources\n## \u2753 Question\r\n\r\nAs 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)... \r\n\r\nThis 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.\r\n\r\n## tldr;\r\nresources 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`)\r\n\r\n<details>\r\n<summary>exact combinations I tested</summary>\r\n\r\n## resources generated with pyside2-rcc v5.14.2  (from pip)\r\n\r\n### using pip to install pyside2/pyqt5\r\n\r\n>PySide2==5.11.2 -> 'Shiboken.ObjectType' object is not iterable\r\n>PySide2==5.12.6 -> 'Shiboken.ObjectType' object is not iterable\r\n>PySide2==5.13.2 -> 'Shiboken.ObjectType' object is not iterable\r\n\r\nPySide2==5.14.0 -> **works**\r\nPySide2==5.14.2.1 -> **works**\r\n\r\n(PyQt5 < 5.11.3 -> uninstallable on pip due to sip)\r\nPyQt5==5.11.3 -> no icons\r\nPyQt5==5.12.3 -> no icons\r\nPyQt5==5.13.2 -> **works**\r\nPyQt5==5.14.2 -> **works**\r\n\r\n### using conda to install pyside2/pyqt5\r\n\r\npyside2=5.12.4.py37h9cca949_0      conda-forge -> no icons\r\npyside2=5.13.2.py37ha8f7116_2     conda-forge -> no icons\r\npyqt=5.9.2.py37h2a560b1_4      conda-forge -> no icons\r\npyqt=5.12.3.py37ha62fc16_3      conda-forge -> no icons\r\n\r\n## resources generated with pyrcc5 v5.14.2  (from pip)\r\n\r\n### using pip to install pyside2/pyqt5\r\n\r\nPySide2==5.14.2.1 -> **works**\r\nPyQt5==5.11.3 -> **works**\r\nPyQt5==5.12.3 -> **works**\r\nPyQt5==5.13.2 -> **works**\r\nPyQt5==5.14.2 -> **works**\r\n\r\n### using conda to install pyside2/pyqt5\r\n\r\npyside2=5.12.4.py37h9cca949_0      conda-forge -> **works**\r\npyside2=5.13.2.py37ha8f7116_2      conda-forge -> **works**\r\npyqt=5.9.2.py37h2a560b1_4      conda-forge -> **works**\r\npyqt=5.12.3.py37ha62fc16_3      conda-forge -> **works**\r\n\r\n</details>\r\n\r\n### question for @napari/core-devs \r\nI 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?\r\n- 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)... \r\n- 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... \ud83d\ude44\n", "before_files": [{"content": "\"\"\"Utility script to generate copies of icons with colors based\non our themes. Neccessary workaround because qt does not allow\nfor styling svg elements using qss\n\"\"\"\n\nimport os\nimport re\nimport shutil\nfrom subprocess import check_call, CalledProcessError\nfrom typing import Dict, List, Tuple\n\nfrom ..utils.theme import palettes as _palettes\n\nRESOURCES_DIR = os.path.abspath(os.path.dirname(__file__))\nSVGPATH = os.path.join(RESOURCES_DIR, 'icons')\n\nsvg_tag_open = re.compile(r'(<svg[^>]*>)')\n\n\ndef themify_icons(\n    dest_dir: str,\n    svg_path: str = SVGPATH,\n    palettes: Dict[str, Dict[str, str]] = _palettes,\n    color_lookup: Dict[str, str] = None,\n) -> List[str]:\n    \"\"\"Create a new \"themed\" SVG file, for every SVG file in ``svg_path``.\n\n    Parameters\n    ----------\n    dest_dir : str\n        The directory in which to write all of the themed icons.  Individual\n        themes will be in subdirectories named after each theme.\n    svg_path : str, optional\n        The folder to look in for SVG files, by default will search in a folder\n        named ``icons`` in the same directory as this file.\n    palettes : dict, optional\n        A mapping of ``theme_name: theme_dict``, where ``theme_dict`` is a\n        mapping of color classes to rgb strings. By default will uses palettes\n        from :const:`napari.resources.utils.theme.palettes`.\n    color_lookup : dict, optional\n        A mapping of icon name to color class.  If the icon name is not in the\n        color_lookup, it's color class will be ``\"icon\"``.\n\n    Returns\n    -------\n    files : list of str\n        a list of generaged SVG filepaths (each relative to dest_dir)\n    \"\"\"\n\n    color_lookup = color_lookup or {\n        'visibility': 'text',\n        'visibility_off': 'highlight',\n        'menu': 'highlight',\n        'drop_down': 'secondary',\n        'plus': 'secondary',\n        'minus': 'secondary',\n        'properties_contract': 'secondary',\n        'properties_expand': 'secondary',\n    }\n    icon_names = [\n        i.replace('.svg', '')\n        for i in os.listdir(SVGPATH)\n        if i.endswith('.svg')\n    ]\n\n    svg_style_insert = r\"\"\"<style type=\"text/css\">\n    path{fill:{{ color }}}\n    polygon{fill:{{ color }}}\n    circle{fill:{{ color }}}\n    rect{fill:{{ color }}}\n    </style>\"\"\"\n\n    files = []\n    for theme_name, palette in palettes.items():\n        palette_dir = os.path.join(dest_dir, theme_name)\n        os.makedirs(palette_dir, exist_ok=True)\n        for icon_name in icon_names:\n            svg_name = icon_name + '.svg'\n            new_file = os.path.join(palette_dir, svg_name)\n            color = color_lookup.get(icon_name, 'icon')\n            css = svg_style_insert.replace('{{ color }}', palette[color])\n            with open(os.path.join(SVGPATH, svg_name), 'r') as fr:\n                contents = fr.read()\n            with open(new_file, 'w') as fw:\n                # use regex to find the svg tag and insert css right after\n                # (the '\\\\1' syntax includes the matched tag in the output)\n                fw.write(svg_tag_open.sub(f'\\\\1{css}', contents))\n            files.append(os.path.join(theme_name, svg_name))\n    return files\n\n\ndef build_resources_qrc(\n    dest_dir: str, overwrite: bool = False\n) -> Tuple[str, str]:\n    \"\"\"Create a res.qrc file for all icons generated by ``themify_icons``.\n\n    Parameters\n    ----------\n    dest_dir : str\n        The directory in which to write create the output file.  Themed SVG\n        icons will also be written to ``dest_dir/themes``.\n    overwrite : bool\n        Whether to force rebuilding of the icons and res.qrc file, by default\n        False\n\n    Returns\n    -------\n    tuple\n        2-tuple of (path-to-qrc.res, path-to-theme-directory).\n    \"\"\"\n    qrc_path = os.path.join(dest_dir, 'res.qrc')\n    theme_dir = os.path.join(dest_dir, 'themes')\n    if os.path.exists(qrc_path) and (not overwrite):\n        return qrc_path, theme_dir\n\n    qrc_string = \"\"\"\n    <!DOCTYPE RCC>\n    <RCC version=\"1.0\">\n    <qresource>\n    \"\"\"\n    for filename in themify_icons(theme_dir):\n        qrc_string += f'\\n    <file>themes/{filename}</file>'\n\n    qrc_string += \"\"\"\n    </qresource>\n    </RCC>\n    \"\"\"\n\n    with open(qrc_path, 'w') as f:\n        f.write(qrc_string)\n\n    return qrc_path, theme_dir\n\n\ndef build_pyqt_resources(out_path: str, overwrite: bool = False) -> str:\n    \"\"\"Build a res.qrc file from icons and convert for python usage.\n\n    calls :func:`build_resources` and then converts using ``pyside2-rcc`` or\n    ``pyrcc5`` depending on which is installed in the environment.\n    Finally, cleans up autogenerated icon.svgs and res.qrc file after _qt.py\n    file is generated\n\n    References\n    ----------\n    https://doc.qt.io/qt-5/resources.html\n\n    Parameters\n    ----------\n    out_path : str\n        Path to write the python resource file.\n    overwrite : bool, optional\n        Whether to force rebuilding of the output file, by default False\n\n    Returns\n    -------\n    out_path : str\n        Path to the python resource file.  Import this file to make the SVGs\n        and other resources available to Qt stylesheets.\n    \"\"\"\n\n    if os.path.exists(out_path) and not overwrite:\n        return out_path\n\n    # build the resource file to the same path\n    qrc_path, theme_dir = build_resources_qrc(\n        os.path.dirname(out_path), overwrite=overwrite\n    )\n\n    # then convert it to a python file\n    try:\n        check_call(['pyrcc5', '-o', out_path, qrc_path])\n    except (FileNotFoundError, CalledProcessError):\n        check_call(['pyside2-rcc', '-o', out_path, qrc_path])\n\n    # make sure we import from qtpy\n    with open(out_path, \"rt\") as fin:\n        data = fin.read()\n        data = data.replace('PySide2', 'qtpy').replace('PyQt5', 'qtpy')\n    with open(out_path, \"wt\") as fin:\n        fin.write(data)\n\n    # cleanup.\n    # we do this here because pip uninstall napari would not collect these\n    # and the final `out_path.py` contains all the necessary bytes info\n    shutil.rmtree(theme_dir, ignore_errors=True)\n    try:\n        os.remove(qrc_path)\n    except Exception:\n        pass\n    return out_path\n", "path": "napari/resources/build_icons.py"}], "after_files": [{"content": "\"\"\"Utility script to generate copies of icons with colors based\non our themes. Neccessary workaround because qt does not allow\nfor styling svg elements using qss\n\"\"\"\n\nimport os\nimport re\nimport shutil\nfrom subprocess import check_call\nfrom typing import Dict, List, Tuple\n\nfrom ..utils.theme import palettes as _palettes\n\nRESOURCES_DIR = os.path.abspath(os.path.dirname(__file__))\nSVGPATH = os.path.join(RESOURCES_DIR, 'icons')\n\nsvg_tag_open = re.compile(r'(<svg[^>]*>)')\n\n\ndef themify_icons(\n    dest_dir: str,\n    svg_path: str = SVGPATH,\n    palettes: Dict[str, Dict[str, str]] = _palettes,\n    color_lookup: Dict[str, str] = None,\n) -> List[str]:\n    \"\"\"Create a new \"themed\" SVG file, for every SVG file in ``svg_path``.\n\n    Parameters\n    ----------\n    dest_dir : str\n        The directory in which to write all of the themed icons.  Individual\n        themes will be in subdirectories named after each theme.\n    svg_path : str, optional\n        The folder to look in for SVG files, by default will search in a folder\n        named ``icons`` in the same directory as this file.\n    palettes : dict, optional\n        A mapping of ``theme_name: theme_dict``, where ``theme_dict`` is a\n        mapping of color classes to rgb strings. By default will uses palettes\n        from :const:`napari.resources.utils.theme.palettes`.\n    color_lookup : dict, optional\n        A mapping of icon name to color class.  If the icon name is not in the\n        color_lookup, it's color class will be ``\"icon\"``.\n\n    Returns\n    -------\n    files : list of str\n        a list of generaged SVG filepaths (each relative to dest_dir)\n    \"\"\"\n\n    color_lookup = color_lookup or {\n        'visibility': 'text',\n        'visibility_off': 'highlight',\n        'menu': 'highlight',\n        'drop_down': 'secondary',\n        'plus': 'secondary',\n        'minus': 'secondary',\n        'properties_contract': 'secondary',\n        'properties_expand': 'secondary',\n    }\n    icon_names = [\n        i.replace('.svg', '')\n        for i in os.listdir(SVGPATH)\n        if i.endswith('.svg')\n    ]\n\n    svg_style_insert = r\"\"\"<style type=\"text/css\">\n    path{fill:{{ color }}}\n    polygon{fill:{{ color }}}\n    circle{fill:{{ color }}}\n    rect{fill:{{ color }}}\n    </style>\"\"\"\n\n    files = []\n    for theme_name, palette in palettes.items():\n        palette_dir = os.path.join(dest_dir, theme_name)\n        os.makedirs(palette_dir, exist_ok=True)\n        for icon_name in icon_names:\n            svg_name = icon_name + '.svg'\n            new_file = os.path.join(palette_dir, svg_name)\n            color = color_lookup.get(icon_name, 'icon')\n            css = svg_style_insert.replace('{{ color }}', palette[color])\n            with open(os.path.join(SVGPATH, svg_name), 'r') as fr:\n                contents = fr.read()\n            with open(new_file, 'w') as fw:\n                # use regex to find the svg tag and insert css right after\n                # (the '\\\\1' syntax includes the matched tag in the output)\n                fw.write(svg_tag_open.sub(f'\\\\1{css}', contents))\n            files.append(os.path.join(theme_name, svg_name))\n    return files\n\n\ndef build_resources_qrc(\n    dest_dir: str, overwrite: bool = False\n) -> Tuple[str, str]:\n    \"\"\"Create a res.qrc file for all icons generated by ``themify_icons``.\n\n    Parameters\n    ----------\n    dest_dir : str\n        The directory in which to write create the output file.  Themed SVG\n        icons will also be written to ``dest_dir/themes``.\n    overwrite : bool\n        Whether to force rebuilding of the icons and res.qrc file, by default\n        False\n\n    Returns\n    -------\n    tuple\n        2-tuple of (path-to-qrc.res, path-to-theme-directory).\n    \"\"\"\n    qrc_path = os.path.join(dest_dir, 'res.qrc')\n    theme_dir = os.path.join(dest_dir, 'themes')\n    if os.path.exists(qrc_path) and (not overwrite):\n        return qrc_path, theme_dir\n\n    qrc_string = \"\"\"\n    <!DOCTYPE RCC>\n    <RCC version=\"1.0\">\n    <qresource>\n    \"\"\"\n    for filename in themify_icons(theme_dir):\n        qrc_string += f'\\n    <file>themes/{filename}</file>'\n\n    qrc_string += \"\"\"\n    </qresource>\n    </RCC>\n    \"\"\"\n\n    with open(qrc_path, 'w') as f:\n        f.write(qrc_string)\n\n    return qrc_path, theme_dir\n\n\ndef build_pyqt_resources(out_path: str, overwrite: bool = False) -> str:\n    \"\"\"Build a res.qrc file from icons and convert for python usage.\n\n    calls :func:`build_resources` and then converts using ``pyside2-rcc`` or\n    ``pyrcc5`` depending on which is installed in the environment.\n    Finally, cleans up autogenerated icon.svgs and res.qrc file after _qt.py\n    file is generated\n\n    References\n    ----------\n    https://doc.qt.io/qt-5/resources.html\n\n    Parameters\n    ----------\n    out_path : str\n        Path to write the python resource file.\n    overwrite : bool, optional\n        Whether to force rebuilding of the output file, by default False\n\n    Returns\n    -------\n    out_path : str\n        Path to the python resource file.  Import this file to make the SVGs\n        and other resources available to Qt stylesheets.\n    \"\"\"\n\n    if os.path.exists(out_path) and not overwrite:\n        return out_path\n\n    # build the resource file to the same path\n    qrc_path, theme_dir = build_resources_qrc(\n        os.path.dirname(out_path), overwrite=overwrite\n    )\n\n    # then convert it to a python file\n    # this looks a little funny, but rather than pick the exact binary to try\n    # once, we use this try/catch loop because\n    # 1. we always want to use pyrcc5 if it's available, regardless of API\n    # 2. it will sometimes, (if not always) be named pyrcc5.bat on windows...\n    # 3. only then do we try pyside2-rcc\n    # see https://github.com/napari/napari/issues/1221 for background\n    for binary in ('pyrcc5.bat', 'pyrcc5', 'pyside2-rcc'):\n        try:\n            check_call([binary, '-o', out_path, qrc_path])\n        except FileNotFoundError:\n            continue\n        break\n    else:\n        raise FileNotFoundError(\n            \"Unable to find an executable to build Qt resources (icons).\\n\"\n            \"Tried: 'pyrcc5.bat', 'pyrcc5', 'pyside2-rcc'.\\n\"\n            \"Please open issue at https://github.com/napari/napari/issues/.\"\n        )\n\n    # make sure we import from qtpy\n    with open(out_path, \"rt\") as fin:\n        data = fin.read()\n        data = data.replace('PySide2', 'qtpy').replace('PyQt5', 'qtpy')\n    with open(out_path, \"wt\") as fin:\n        fin.write(data)\n\n    # cleanup.\n    # we do this here because pip uninstall napari would not collect these\n    # and the final `out_path.py` contains all the necessary bytes info\n    shutil.rmtree(theme_dir, ignore_errors=True)\n    try:\n        os.remove(qrc_path)\n    except Exception:\n        pass\n    return out_path\n", "path": "napari/resources/build_icons.py"}]}