Repository: napari/napari

Issue:
Use QDialog accept/reject instead of custom signals
## 🧰 Task
In looking through some settings stuff, I see this pattern being used in a few places, where a custom `valueChanged` signal is added to a `QDialog`, and emitted just before closing the dialog, based on whether the user clicked ok or cancel:

https://github.com/napari/napari/blob/d6bc683b019c4a3a3c6e936526e29bbd59cca2f4/napari/_qt/dialogs/qt_message_dialogs.py#L47-L59

usage example here:
https://github.com/napari/napari/blob/d6bc683b019c4a3a3c6e936526e29bbd59cca2f4/napari/_qt/dialogs/preferences_dialog.py#L191-L196

QDialogs run in `exec` mode (modal dialogs) already [have the concept of "accept" and "reject"](https://doc.qt.io/qt-5/qdialog.html#modal-dialogs), so this is reinventing the wheel a bit.  Instead of hooking up new internal signals to various potential outcomes, you can use the [`DialogCode`](https://doc.qt.io/qt-5/qdialog.html#DialogCode-enum) returned from the [`exec`](https://doc.qt.io/qt-5/qdialog.html#exec) method.

In this case, you can get rid of the whole `ConfirmDialog` class and do it with a single static dialog:

```py
ret = QMessageBox.question(
    None,
    "Dialog Title",
    "Are you sure you want to restore default settings?",
    QMessageBox.RestoreDefaults | QMessageBox.Cancel,
    QMessageBox.RestoreDefaults,
)
if ret == QMessageBox.RestoreDefault:
    _reset_widgets()
   # and then modify _reset_widgets to not take an event
```

cc @ppwadhwa 




Discussion:
I'm supportive of this change

Resolving patch:
diff --git a/napari/_qt/dialogs/preferences_dialog.py b/napari/_qt/dialogs/preferences_dialog.py
--- a/napari/_qt/dialogs/preferences_dialog.py
+++ b/napari/_qt/dialogs/preferences_dialog.py
@@ -6,6 +6,7 @@
     QDialog,
     QHBoxLayout,
     QListWidget,
+    QMessageBox,
     QPushButton,
     QStackedWidget,
     QVBoxLayout,
@@ -14,7 +15,7 @@
 from ..._vendor.qt_json_builder.qt_jsonschema_form import WidgetBuilder
 from ...settings import get_settings
 from ...utils.translations import trans
-from .qt_message_dialogs import ConfirmDialog, ResetNapariInfoDialog
+from .qt_message_dialogs import ResetNapariInfoDialog
 
 if TYPE_CHECKING:
     from pydantic.fields import ModelField
@@ -190,14 +191,19 @@ def get_page_dict(self, field: 'ModelField'):
 
     def restore_defaults(self):
         """Launches dialog to confirm restore settings choice."""
-        self._reset_dialog = ConfirmDialog(
-            parent=self,
-            text=trans._("Are you sure you want to restore default settings?"),
+
+        response = QMessageBox.question(
+            self,
+            trans._("Restore Settings"),
+            trans._("Are you sure you want to restore default settings?"),
+            QMessageBox.RestoreDefaults | QMessageBox.Cancel,
+            QMessageBox.RestoreDefaults,
         )
-        self._reset_dialog.valueChanged.connect(self._reset_widgets)
-        self._reset_dialog.exec_()
 
-    def _reset_widgets(self, event=None):
+        if response == QMessageBox.RestoreDefaults:
+            self._reset_widgets()
+
+    def _reset_widgets(self):
         """Deletes the widgets and rebuilds with defaults.
 
         Parameter
@@ -209,21 +215,20 @@ def _reset_widgets(self, event=None):
 
         """
 
-        if event is True:
-            get_settings().reset()
-            self.accept()
-            self.valueChanged.emit()
-            self._list.clear()
+        get_settings().reset()
+        self.accept()
+        self._list.clear()
 
-            for n in range(self._stack.count()):
-                widget = self._stack.removeWidget(  # noqa: F841
-                    self._stack.currentWidget()
-                )
-                del widget
+        for n in range(self._stack.count()):
+            widget = self._stack.removeWidget(  # noqa: F841
+                self._stack.currentWidget()
+            )
+            del widget
 
-            self.make_dialog()
-            self._list.setCurrentRow(0)
-            self.show()
+        self.make_dialog()
+        self._list.setCurrentRow(0)
+        self.valueChanged.emit()
+        self.show()
 
     def on_click_ok(self):
         """Keeps the selected preferences saved to settings."""
diff --git a/napari/_qt/dialogs/qt_message_dialogs.py b/napari/_qt/dialogs/qt_message_dialogs.py
--- a/napari/_qt/dialogs/qt_message_dialogs.py
+++ b/napari/_qt/dialogs/qt_message_dialogs.py
@@ -2,7 +2,6 @@
 from qtpy.QtWidgets import (
     QDialog,
     QGridLayout,
-    QHBoxLayout,
     QLabel,
     QPushButton,
     QVBoxLayout,
@@ -12,53 +11,6 @@
 from ...utils.translations import trans
 
 
-class ConfirmDialog(QDialog):
-    """Dialog to confirms a user's choice to restore default settings."""
-
-    valueChanged = Signal(bool)
-
-    def __init__(
-        self,
-        parent: QWidget = None,
-        text: str = "",
-    ):
-        super().__init__(parent)
-
-        # Set up components
-        self._question = QLabel(self)
-        self._button_restore = QPushButton(trans._("Restore"))
-        self._button_cancel = QPushButton(trans._("Cancel"))
-        self._button_restore.setDefault(True)
-
-        # Widget set up
-        self._question.setText(text)
-
-        # Layout
-        button_layout = QHBoxLayout()
-        button_layout.addWidget(self._button_cancel)
-        button_layout.addWidget(self._button_restore)
-
-        main_layout = QVBoxLayout()
-        main_layout.addWidget(self._question)
-        main_layout.addLayout(button_layout)
-
-        self.setLayout(main_layout)
-
-        # Signals
-        self._button_cancel.clicked.connect(self.on_click_cancel)
-        self._button_restore.clicked.connect(self.on_click_restore)
-
-    def on_click_cancel(self):
-        """Do not restore defaults and close window."""
-        self.valueChanged.emit(False)
-        self.close()
-
-    def on_click_restore(self):
-        """Emit signal to restore defaults and close window."""
-        self.valueChanged.emit(True)
-        self.close()
-
-
 class ResetNapariInfoDialog(QDialog):
     """Dialog to inform the user that restart of Napari is necessary to enable setting."""
 
diff --git a/napari/_qt/widgets/qt_keyboard_settings.py b/napari/_qt/widgets/qt_keyboard_settings.py
--- a/napari/_qt/widgets/qt_keyboard_settings.py
+++ b/napari/_qt/widgets/qt_keyboard_settings.py
@@ -12,6 +12,7 @@
     QKeySequenceEdit,
     QLabel,
     QLineEdit,
+    QMessageBox,
     QPushButton,
     QTableWidget,
     QTableWidgetItem,
@@ -24,7 +25,6 @@
 from ...utils.action_manager import action_manager
 from ...utils.interactions import Shortcut
 from ...utils.translations import trans
-from ..dialogs.qt_message_dialogs import ConfirmDialog
 from ..qt_resources import get_stylesheet
 
 # Dict used to format strings returned from converted key press events.
@@ -72,7 +72,7 @@ def __init__(
         self._table.setSelectionBehavior(QAbstractItemView.SelectItems)
         self._table.setSelectionMode(QAbstractItemView.SingleSelection)
         self._table.setShowGrid(False)
-        self._restore_button = QPushButton(trans._("Reset All Keybindings"))
+        self._restore_button = QPushButton(trans._("Restore All Keybindings"))
 
         # Set up dictionary for layers and associated actions.
         all_actions = action_manager._actions.copy()
@@ -119,16 +119,18 @@ def __init__(
     def restore_defaults(self):
         """Launches dialog to confirm restore choice."""
 
-        self._reset_dialog = ConfirmDialog(
-            parent=self,
-            text=trans._(
-                "Are you sure you want to restore default shortcuts?"
-            ),
+        response = QMessageBox.question(
+            self,
+            trans._("Restore Shortcuts"),
+            trans._("Are you sure you want to restore default shortcuts?"),
+            QMessageBox.RestoreDefaults | QMessageBox.Cancel,
+            QMessageBox.RestoreDefaults,
         )
-        self._reset_dialog.valueChanged.connect(self._reset_shortcuts)
-        self._reset_dialog.exec_()
 
-    def _reset_shortcuts(self, event=None):
+        if response == QMessageBox.RestoreDefaults:
+            self._reset_shortcuts()
+
+    def _reset_shortcuts(self):
         """Reset shortcuts to default settings.
 
         Parameters
@@ -137,18 +139,16 @@ def _reset_shortcuts(self, event=None):
             Event will indicate whether user confirmed resetting shortcuts.
         """
 
-        # event is True if the user confirmed reset shortcuts
-        if event is True:
-            get_settings().shortcuts.reset()
-            for (
-                action,
-                shortcuts,
-            ) in get_settings().shortcuts.shortcuts.items():
-                action_manager.unbind_shortcut(action)
-                for shortcut in shortcuts:
-                    action_manager.bind_shortcut(action, shortcut)
-
-            self._set_table(layer_str=self.layer_combo_box.currentText())
+        get_settings().shortcuts.reset()
+        for (
+            action,
+            shortcuts,
+        ) in get_settings().shortcuts.shortcuts.items():
+            action_manager.unbind_shortcut(action)
+            for shortcut in shortcuts:
+                action_manager.bind_shortcut(action, shortcut)
+
+        self._set_table(layer_str=self.layer_combo_box.currentText())
 
     def _set_table(self, layer_str=''):
         """Builds and populates keybindings table.


Test patch: