Repository: napari/napari

Issue:
Remove keybindings dialog
## 🧰 Task
<!-- A clear and concise description of the task -->
Now that keybindings are configurable in the preferences we should remove the keybindings dialog, currently accessible via "help".

<img width="512" alt="Screen Shot 2021-07-08 at 5 23 47 PM" src="https://user-images.githubusercontent.com/6531703/125005723-4a447b80-e011-11eb-9436-c0f81f8ab7ac.png">
 


Discussion:

Resolving patch:
diff --git a/napari/_qt/dialogs/qt_about_key_bindings.py b/napari/_qt/dialogs/qt_about_key_bindings.py
deleted file mode 100644
--- a/napari/_qt/dialogs/qt_about_key_bindings.py
+++ /dev/null
@@ -1,152 +0,0 @@
-from collections import OrderedDict
-
-from qtpy.QtCore import Qt
-from qtpy.QtWidgets import (
-    QComboBox,
-    QDialog,
-    QHBoxLayout,
-    QTextEdit,
-    QVBoxLayout,
-)
-
-import napari
-
-from ...utils.action_manager import action_manager
-from ...utils.interactions import get_key_bindings_summary
-from ...utils.theme import get_theme
-from ...utils.translations import trans
-
-
-class QtAboutKeyBindings(QDialog):
-    """Qt dialog window for displaying keybinding information.
-
-    Parameters
-    ----------
-    viewer : napari.components.ViewerModel
-        Napari viewer containing the rendered scene, layers, and controls.
-
-    key_map_handler : napari.utils.key_bindings.KeyMapHandler
-        Handler for key mapping and calling functionality.
-
-    Attributes
-    ----------
-    key_bindings_strs : collections.OrderedDict
-        Ordered dictionary of hotkey shortcuts and associated key bindings.
-        Dictionary keys include:
-        - 'All active key bindings'
-        - 'Image layer'
-        - 'Labels layer'
-        - 'Points layer'
-        - 'Shapes layer'
-        - 'Surface layer'
-        - 'Vectors layer'
-    layout : qtpy.QtWidgets.QVBoxLayout
-        Layout of the widget.
-    layerTypeComboBox : qtpy.QtWidgets.QComboBox
-        Dropdown menu to select layer type.
-    textEditBox : qtpy.QtWidgets.QTextEdit
-        Text box widget containing table of key bindings information.
-    viewer : napari.components.ViewerModel
-        Napari viewer containing the rendered scene, layers, and controls.
-    """
-
-    ALL_ACTIVE_KEYBINDINGS = trans._('All active key bindings')
-
-    def __init__(self, viewer, key_map_handler, parent=None):
-        super().__init__(parent=parent)
-
-        self.viewer = viewer
-        self.layout = QVBoxLayout()
-
-        self.setWindowTitle(trans._('Keybindings'))
-        self.setWindowModality(Qt.NonModal)
-        self.setLayout(self.layout)
-
-        # stacked key bindings widgets
-        self.textEditBox = QTextEdit()
-        self.textEditBox.setTextInteractionFlags(Qt.TextSelectableByMouse)
-        self.textEditBox.setMinimumWidth(360)
-
-        # Can switch to a normal dict when our minimum Python is 3.7
-        self.key_bindings_strs = OrderedDict()
-        self.key_bindings_strs[self.ALL_ACTIVE_KEYBINDINGS] = ''
-        self.key_map_handler = key_map_handler
-        theme = get_theme(self.viewer.theme)
-
-        col = theme['secondary']
-        layers = [
-            napari.layers.Image,
-            napari.layers.Labels,
-            napari.layers.Points,
-            napari.layers.Shapes,
-            napari.layers.Surface,
-            napari.layers.Vectors,
-        ]
-        layer_shortcuts = action_manager._get_layer_shortcuts(layers)
-        for layer in layers:
-            if len(layer.class_keymap) == 0:
-                text = trans._('No key bindings')
-            else:
-                text = get_key_bindings_summary(
-                    layer_shortcuts[layer], col=col
-                )
-
-            # TODO: Add localization. Add localized layer name to layer types.
-            self.key_bindings_strs[f"{layer.__name__} layer"] = text
-
-        # layer type selection
-        self.layerTypeComboBox = QComboBox()
-        self.layerTypeComboBox.addItems(list(self.key_bindings_strs))
-
-        self.layerTypeComboBox.activated[str].connect(self.change_layer_type)
-        self.layerTypeComboBox.setCurrentText(self.ALL_ACTIVE_KEYBINDINGS)
-        layer_type_layout = QHBoxLayout()
-        layer_type_layout.setContentsMargins(10, 5, 0, 0)
-        layer_type_layout.addWidget(self.layerTypeComboBox)
-        layer_type_layout.addStretch(1)
-        layer_type_layout.setSpacing(0)
-        self.layout.addLayout(layer_type_layout)
-        self.layout.addWidget(self.textEditBox, 1)
-
-        self.viewer.events.theme.connect(self.update_active_layer)
-        self.update_active_layer()
-
-    def change_layer_type(self, text):
-        """Change layer type selected in dropdown menu.
-
-        Parameters
-        ----------
-        text : str
-            Dictionary key to access key bindings associated with the layer.
-            Available keys include:
-            - 'All active key bindings'
-            - 'Image layer'
-            - 'Labels layer'
-            - 'Points layer'
-            - 'Shapes layer'
-            - 'Surface layer'
-            - 'Vectors layer'
-        """
-        self.textEditBox.setHtml(self.key_bindings_strs[text])
-
-    def update_active_layer(self, event=None):
-        """Update the active layer and display key bindings for that layer type.
-
-        Parameters
-        ----------
-        event : napari.utils.event.Event, optional
-            The napari event that triggered this method, by default None.
-        """
-        theme = get_theme(self.viewer.theme)
-        col = theme['secondary']
-
-        # Add class and instance viewer key bindings
-        active_shortcuts = action_manager._get_active_shortcuts(
-            self.key_map_handler.active_keymap
-        )
-        text = get_key_bindings_summary(active_shortcuts, col=col)
-
-        # Update layer speficic key bindings if all active are displayed
-        self.key_bindings_strs[self.ALL_ACTIVE_KEYBINDINGS] = text
-        if self.layerTypeComboBox.currentText() == self.ALL_ACTIVE_KEYBINDINGS:
-            self.textEditBox.setHtml(text)
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
@@ -921,17 +921,6 @@ def _add_help_menu(self):
         )
         self.help_menu.addAction(about_action)
 
-        about_key_bindings = QAction(
-            trans._("Show Key Bindings"), self._qt_window
-        )
-        about_key_bindings.setShortcut("Ctrl+Alt+/")
-        about_key_bindings.setShortcutContext(Qt.ApplicationShortcut)
-        about_key_bindings.setStatusTip(trans._('key_bindings'))
-        about_key_bindings.triggered.connect(
-            self.qt_viewer.show_key_bindings_dialog
-        )
-        self.help_menu.addAction(about_key_bindings)
-
     def _toggle_activity_dock(self, event):
         is_currently_visible = self._qt_window._activity_dialog.isVisible()
         if not is_currently_visible:
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
@@ -31,7 +31,6 @@
 from ..utils.theme import get_theme
 from ..utils.translations import trans
 from .containers import QtLayerList
-from .dialogs.qt_about_key_bindings import QtAboutKeyBindings
 from .dialogs.screenshot_dialog import ScreenshotDialog
 from .perf.qt_performance import QtPerformance
 from .utils import QImg2array, circle_pixmap, square_pixmap
@@ -120,7 +119,6 @@ def __init__(self, viewer: Viewer, show_welcome_screen: bool = False):
         self.viewerButtons = QtViewerButtons(self.viewer)
         self._key_map_handler = KeymapHandler()
         self._key_map_handler.keymap_providers = [self.viewer]
-        self._key_bindings_dialog = None
         self._console = None
 
         layerList = QWidget()
@@ -387,10 +385,6 @@ def _on_active_change(self, event=None):
             else [self.viewer.layers.selection.active, self.viewer]
         )
 
-        # If a QtAboutKeyBindings exists, update its text.
-        if self._key_bindings_dialog is not None:
-            self._key_bindings_dialog.update_active_layer()
-
     def _on_add_layer_change(self, event):
         """When a layer is added, set its parent and order.
 
@@ -729,17 +723,6 @@ def toggle_console_visibility(self, event=None):
             self.viewerButtons.consoleButton
         )
 
-    def show_key_bindings_dialog(self, event=None):
-        if self._key_bindings_dialog is None:
-            self._key_bindings_dialog = QtAboutKeyBindings(
-                self.viewer, self._key_map_handler, parent=self
-            )
-        # make sure the dialog is shown
-        self._key_bindings_dialog.show()
-        # make sure the the dialog gets focus
-        self._key_bindings_dialog.raise_()  # for macOS
-        self._key_bindings_dialog.activateWindow()  # for Windows
-
     def _map_canvas2world(self, position):
         """Map position from canvas pixels into world coordinates.
 


Test patch:
diff --git a/napari/_qt/dialogs/_tests/test_qt_about_key_bindings.py b/napari/_qt/dialogs/_tests/test_qt_about_key_bindings.py
deleted file mode 100644
--- a/napari/_qt/dialogs/_tests/test_qt_about_key_bindings.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import numpy as np
-
-from napari._qt.dialogs.qt_about_key_bindings import QtAboutKeyBindings
-
-
-def test_about_key_bindings_dialog(make_napari_viewer):
-    """Test creating QtAboutKeyBindings dialog window and its methods."""
-    viewer = make_napari_viewer()
-    view = viewer.window.qt_viewer
-    key_bindings_dialog = QtAboutKeyBindings(viewer, view._key_map_handler)
-    assert key_bindings_dialog.viewer == viewer
-    assert key_bindings_dialog.key_map_handler == view._key_map_handler
-
-    # check ability to update the active layer, should've ran during init too
-    key_bindings_dialog.update_active_layer()
-
-    # check ability to change layer type selected in dropdown
-    for layer in key_bindings_dialog.key_bindings_strs.keys():
-        key_bindings_dialog.change_layer_type(layer)
-        # Could test to make sure text has been updated, but `toHtml` does not
-        # return the same str that is provided to `setHtml`, so this could get
-        # messy...
-
-
-def test_show_key_bindings_dialog(make_napari_viewer, monkeypatch):
-    """Test creating dialog with method of qt_viewer."""
-    viewer = make_napari_viewer()
-    view = viewer.window.qt_viewer
-    # check that dialog does not exist yet
-    assert not view.findChild(QtAboutKeyBindings)
-    # turn off showing the dialog for test
-    monkeypatch.setattr(QtAboutKeyBindings, 'show', lambda *a: None)
-    # create the dialog and make sure that it now exists
-    view.show_key_bindings_dialog()
-    assert isinstance(view.findChild(QtAboutKeyBindings), QtAboutKeyBindings)
-
-
-def test_updating_with_layer_change(make_napari_viewer, monkeypatch):
-    """Test that the dialog text updates when the active layer is changed."""
-    viewer = make_napari_viewer()
-    view = viewer.window.qt_viewer
-    # turn off showing the dialog for test
-    monkeypatch.setattr(QtAboutKeyBindings, 'show', lambda *a: None)
-    view.show_key_bindings_dialog()
-    dialog = view.findChild(QtAboutKeyBindings)
-
-    # add an image layer
-    viewer.add_image(np.random.random((5, 5, 10, 15)))
-    # capture dialog text after active_layer events
-    active_img_layer_text = dialog.textEditBox.toHtml()
-    dialog.update_active_layer()  # force an to update to dialog
-    # check that the text didn't update without a change in the active layer
-    assert dialog.textEditBox.toHtml() == active_img_layer_text
-
-    # add a shape layer (different keybindings)
-    viewer.add_shapes(None, shape_type='polygon')
-    # check that the new layer is the active_layer
-    assert viewer.layers.selection.active == viewer.layers[1]
-    # capture dialog text after active_layer events
-    active_shape_layer_text = dialog.textEditBox.toHtml()
-    # check that the text has changed for the new key bindings
-    assert active_shape_layer_text != active_img_layer_text
-    dialog.update_active_layer()  # force an update to dialog
-    # check that the text didn't update without a change in the active layer
-    assert dialog.textEditBox.toHtml() == active_shape_layer_text