Repository: napari/napari

Issue:
Toggle theme not working
## 🐛 Bug

<!-- A clear and concise description of what the bug is. -->

## To Reproduce

Steps to reproduce the behavior:

1. install napari from master 
2. launch napari
3. chose View > Toggle theme 

<!-- If you have a code sample, error messages, stack traces, please provide it here as well -->
```
Traceback (most recent call last):
  File "/Users/gbokota/Documents/projekty/napari/napari/components/viewer_model.py", line 418, in _toggle_theme
    self.theme = theme_names[(cur_theme + 1) % len(theme_names)]
  File "/Users/gbokota/Documents/projekty/napari/napari/components/viewer_model.py", line 130, in theme
    self.palette = self.themes[theme]
  File "/Users/gbokota/Documents/projekty/napari/napari/components/viewer_model.py", line 114, in palette
    self.events.palette()
  File "/Users/gbokota/Documents/projekty/napari/napari/utils/event.py", line 508, in __call__
    self._invoke_callback(cb, event)
  File "/Users/gbokota/Documents/projekty/napari/napari/utils/event.py", line 529, in _invoke_callback
    cb_event=(cb, event),
  File "/Users/gbokota/Documents/projekty/napari/napari/utils/event.py", line 523, in _invoke_callback
    cb(event)
  File "/Users/gbokota/Documents/projekty/napari/napari/_qt/qt_main_window.py", line 99, in <lambda>
    lambda event: self._update_palette(event.palette)
AttributeError: 'Event' object has no attribute 'palette'
```
## Expected behavior

<!-- A clear and concise description of what you expected to happen. -->
theme changed to bright
## Environment

 - Please copy and paste the information at napari info option in help menubar here:
napari: 0.2.12+25.g51cedac.dirty
Platform: Darwin-18.7.0-x86_64-i386-64bit
Python: 3.7.4 (default, Sep 15 2019, 11:29:11) [Clang 10.0.1 (clang-1001.0.46.4)]
Qt: 5.14.1
PySide2: 5.14.1
VisPy: 0.6.4
NumPy: 1.18.2
SciPy: 1.4.1
scikit-image: 0.16.2
Dask: 2.12.0

GL version: 2.1 ATI-2.11.21
MAX_TEXTURE_SIZE: 16384
 - Any other relevant information:

## Additional context

<!-- Add any other context about the problem here. -->
Observed on all systems.


Discussion:

Resolving patch:
diff --git a/napari/_qt/qt_main_window.py b/napari/_qt/qt_main_window.py
--- a/napari/_qt/qt_main_window.py
+++ b/napari/_qt/qt_main_window.py
@@ -86,7 +86,7 @@ def __init__(self, qt_viewer, *, show=True):
         self._qt_center.layout().addWidget(self.qt_viewer)
         self._qt_center.layout().setContentsMargins(4, 0, 4, 0)
 
-        self._update_palette(qt_viewer.viewer.palette)
+        self._update_palette()
 
         self._add_viewer_dock_widget(self.qt_viewer.dockConsole)
         self._add_viewer_dock_widget(self.qt_viewer.dockLayerControls)
@@ -95,9 +95,7 @@ def __init__(self, qt_viewer, *, show=True):
         self.qt_viewer.viewer.events.status.connect(self._status_changed)
         self.qt_viewer.viewer.events.help.connect(self._help_changed)
         self.qt_viewer.viewer.events.title.connect(self._title_changed)
-        self.qt_viewer.viewer.events.palette.connect(
-            lambda event: self._update_palette(event.palette)
-        )
+        self.qt_viewer.viewer.events.palette.connect(self._update_palette)
 
         if show:
             self.show()
@@ -313,16 +311,11 @@ def show(self):
             self._qt_window.raise_()  # for macOS
             self._qt_window.activateWindow()  # for Windows
 
-    def _update_palette(self, palette):
-        """Update widget color palette.
-
-        Parameters
-        ----------
-        palette : qtpy.QtGui.QPalette
-            Color palette for each widget state (Active, Disabled, Inactive).
-        """
+    def _update_palette(self, event=None):
+        """Update widget color palette."""
         # set window styles which don't use the primary stylesheet
         # FIXME: this is a problem with the stylesheet not using properties
+        palette = self.qt_viewer.viewer.palette
         self._status_bar.setStyleSheet(
             template(
                 'QStatusBar { background: {{ background }}; '
diff --git a/napari/_qt/qt_viewer.py b/napari/_qt/qt_viewer.py
--- a/napari/_qt/qt_viewer.py
+++ b/napari/_qt/qt_viewer.py
@@ -120,6 +120,11 @@ def __init__(self, viewer):
             shortcut='Ctrl+Shift+C',
         )
         self.dockConsole.setVisible(False)
+        # because the console is loaded lazily in the @getter, this line just
+        # gets (or creates) the console when the dock console is made visible.
+        self.dockConsole.visibilityChanged.connect(
+            lambda visible: self.console if visible else None
+        )
         self.dockLayerControls.visibilityChanged.connect(self._constrain_width)
         self.dockLayerList.setMaximumWidth(258)
         self.dockLayerList.setMinimumWidth(258)
@@ -166,7 +171,7 @@ def __init__(self, viewer):
             'standard': QCursor(),
         }
 
-        self._update_palette(None)
+        self._update_palette()
 
         self.viewer.events.interactive.connect(self._on_interactive)
         self.viewer.events.cursor.connect(self._on_cursor)
@@ -197,7 +202,7 @@ def console(self):
     def console(self, console):
         self._console = console
         self.dockConsole.widget = console
-        self._update_palette(None)
+        self._update_palette()
 
     def _constrain_width(self, event):
         """Allow the layer controls to be wider, only if floated.
@@ -397,7 +402,7 @@ def _on_reset_view(self, event):
             # Assumes default camera has the same properties as PanZoomCamera
             self.view.camera.rect = event.rect
 
-    def _update_palette(self, event):
+    def _update_palette(self, event=None):
         """Update the napari GUI theme."""
         # template and apply the primary stylesheet
         themed_stylesheet = template(
@@ -410,7 +415,7 @@ def _update_palette(self, event):
         self.setStyleSheet(themed_stylesheet)
         self.canvas.bgcolor = self.viewer.palette['canvas']
 
-    def toggle_console_visibility(self, event):
+    def toggle_console_visibility(self, event=None):
         """Toggle console visible and not visible.
 
         Imports the console the first time it is requested.


Test patch:
diff --git a/napari/_tests/test_viewer.py b/napari/_tests/test_viewer.py
--- a/napari/_tests/test_viewer.py
+++ b/napari/_tests/test_viewer.py
@@ -125,3 +125,15 @@ def layer_update(*, update_period, num_updates):
     viewer.update(layer_update, update_period=0.01, num_updates=100)
     # the previous time.sleep() that used to be here has been replaced with
     # QtViewer.pool.waitForDone() in the closeEvent of the QtViewer.
+
+
+def test_changing_theme(viewer_factory):
+    """Test instantiating viewer."""
+    view, viewer = viewer_factory()
+    assert viewer.palette['folder'] == 'dark'
+
+    viewer.theme = 'light'
+    assert viewer.palette['folder'] == 'light'
+
+    with pytest.raises(ValueError):
+        viewer.theme = 'nonexistent_theme'