Repository: napari/napari

Issue:
Maybe add DockWidget > should (re)show a hidden widget.
## 🐛 Bug
If someone adds a widget with  `Add Dock Widget >` and then clicks on the `x` to close the widget, they may well have no idea that a new menu item has been added to the `Window` menu... and then when they try create a new "instance" with Dock Widget, all that happens is they get a "Dock widget already added" warning.

That's a bit confusing, I think we all liked the window menu item, but would be great if that plugins menu action could re-show a hidden widget (or, at the very least, tell them where they now have to go to show it again)


Discussion:
yeah, i have found this pattern a little odd too in actual usage! I've by mistake gone back and clicked on that other one. As we said at the time though this is easy to change. I like the solution that we just `re-show` the hidden widget. Should be a simple enough fix.
I agree that it is a bit confusing if the main way to reopen a widget is via a menu item that is added automatically. How does the user delete an instance of a running plugin? I think depending on whether the user is used to Mac OS or Windows may influence the behavior they expect when clicking the `x` (i.e., Mac OS often leaves apps running when closing the window with the `x`)

edit: sorry, accidentally closed the issue!
I agree with Talley's proposed fix. As a general rule, no-ops when "the machine" knows exactly what *should* be done are super frustrating! Just do the thing! 😂 Even if the message says "btw, there's a shortcut for you in the Window menu!"

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
@@ -5,7 +5,6 @@
 import inspect
 import os
 import time
-import warnings
 from collections import Counter
 from itertools import chain, repeat
 from typing import Dict
@@ -559,7 +558,7 @@ def _add_plugin_dock_widget(self, key):
 
         full_name = plugins.menu_item_template.format(*key)
         if full_name in self._dock_widgets:
-            warnings.warn(f'Dock widget {key!r} already added')
+            self._dock_widgets[full_name].show()
             return
 
         Widget, dock_kwargs = plugins.dock_widgets[key]
@@ -597,7 +596,7 @@ def _add_plugin_function_widget(self, key):
         """
         full_name = plugins.menu_item_template.format(*key)
         if full_name in self._dock_widgets:
-            warnings.warn(f'Dock widget {key!r} already added')
+            self._dock_widgets[full_name].show()
             return
 
         func = plugins.function_widgets[key]


Test patch:
diff --git a/napari/_qt/_tests/test_plugin_widgets.py b/napari/_qt/_tests/test_plugin_widgets.py
--- a/napari/_qt/_tests/test_plugin_widgets.py
+++ b/napari/_qt/_tests/test_plugin_widgets.py
@@ -121,9 +121,8 @@ def test_making_plugin_dock_widgets(test_plugin_widgets, make_napari_viewer):
     assert isinstance(dw.widget(), Widg3)
     # This widget uses the parameter annotation method to receive a viewer
     assert isinstance(dw.widget().viewer, napari.Viewer)
-    # Cannot add twice
-    with pytest.warns(UserWarning):
-        actions[1].trigger()
+    # Add twice is ok, only does a show
+    actions[1].trigger()
 
     # trigger the 'TestP1 > Widg2' action (it's in a submenu)
     action = actions[0].menu().actions()[1]
@@ -135,9 +134,8 @@ def test_making_plugin_dock_widgets(test_plugin_widgets, make_napari_viewer):
     assert isinstance(dw.widget(), Widg2)
     # This widget uses parameter *name* "napari_viewer" to get a viewer
     assert isinstance(dw.widget().viewer, napari.Viewer)
-    # Cannot add twice
-    with pytest.warns(UserWarning):
-        action.trigger()
+    # Add twice is ok, only does a show
+    action.trigger()
 
 
 def test_making_function_dock_widgets(test_plugin_widgets, make_napari_viewer):
@@ -163,9 +161,8 @@ def test_making_function_dock_widgets(test_plugin_widgets, make_napari_viewer):
     assert isinstance(magic_widget.viewer.value, napari.Viewer)
     # The function just returns the viewer... make sure we can call it
     assert isinstance(magic_widget(), napari.Viewer)
-    # Cannot add twice
-    with pytest.warns(UserWarning):
-        actions[2].trigger()
+    # Add twice is ok, only does a show
+    actions[2].trigger()
 
 
 def test_clear_all_plugin_widgets(test_plugin_widgets, make_napari_viewer):