Repository: napari/napari

Issue:
Plugin installation dialog "hang" after uninstall
## 🐛 Bug

After uninstalling a plugin, the installation dialog keeps refreshing and effectively prevents any action from happening.

## To Reproduce

Steps to reproduce the behavior:

1. install a plugin from napari plugin dialog (optional)
2. uninstall the plugin installed from previous step
3. see the plugin dialog keeps refreshing

## Expected behavior

The plugin dialog should show the latest status instead of hanging in refreshing

## Environment

 - main branch napari or 0.4.17 rc both have this behavior
 - this is discovered on macOS, not sure if can be reproduced elsewhere




Discussion:
Which OS?
I cannot replicate this on macOS 12.6 arm64.
Uninstalling a plugin seems normal, no refreshing.
Edit: whoops spoke too soon!
Uninstalling a regular plugin is fine, but those with the `npe2` label causes the chain refresh and requires killing napari. Pretty annoying!
Edit2: I found this printed in my terminal, in case it's a clue:
```
/Users/piotrsobolewski/Dev/miniforge3/envs/apple-TF29/lib/python3.9/site-packages/napari_plugin_engine/manager.py:490: UserWarning: 'No plugin found with the name napari-aicsimageio'!
```
(that is the plugin that I uninstalled, that was labeled `npe2`)
It looks like part of problem source may be this dialog:
![obraz](https://user-images.githubusercontent.com/3826210/194403965-3ee8b2e9-fe8f-449a-94f8-18237541f404.png)

That blocks interactions with GUI. But there is still 100% processor usage. 

> It looks like part of problem source may be this dialog: ![obraz](https://user-images.githubusercontent.com/3826210/194403965-3ee8b2e9-fe8f-449a-94f8-18237541f404.png)
> 
> That blocks interactions with GUI. But there is still 100% processor usage.

The refreshing happens still after dismissing this dialog, I am not sure if this dialog is the culprit or the victim of the refreshing at the moment. I can see both ways
Close this dialog allows us to perform some action, but this is not the core of the problem, as everything is laggy. 

Resolving patch:
diff --git a/napari/_qt/dialogs/qt_plugin_dialog.py b/napari/_qt/dialogs/qt_plugin_dialog.py
--- a/napari/_qt/dialogs/qt_plugin_dialog.py
+++ b/napari/_qt/dialogs/qt_plugin_dialog.py
@@ -293,8 +293,13 @@ def _uninstall(
 
         process.start()
 
+        pm2 = PluginManager.instance()
+
         for pkg in pkg_list:
-            plugin_manager.unregister(pkg)
+            if pkg in pm2:
+                pm2.unregister(pkg)
+            else:
+                plugin_manager.unregister(pkg)
 
         return process
 
@@ -781,8 +786,6 @@ def refresh(self):
 
         from ...plugins import plugin_manager
 
-        plugin_manager.discover()  # since they might not be loaded yet
-
         self.already_installed = set()
 
         def _add_to_installed(distname, enabled, npe_version=1):
@@ -816,6 +819,7 @@ def _add_to_installed(distname, enabled, npe_version=1):
             )
 
         pm2 = PluginManager.instance()
+        discovered = pm2.discover()
         for manifest in pm2.iter_manifests():
             distname = normalized_name(manifest.name or '')
             if distname in self.already_installed or distname == 'napari':
@@ -825,6 +829,7 @@ def _add_to_installed(distname, enabled, npe_version=1):
             npev = 'shim' if manifest.npe1_shim else 2
             _add_to_installed(distname, enabled, npe_version=npev)
 
+        plugin_manager.discover()  # since they might not be loaded yet
         for (
             plugin_name,
             _mod_name,
@@ -866,6 +871,14 @@ def _add_to_installed(distname, enabled, npe_version=1):
         self.worker.finished.connect(self._update_count_in_label)
         self.worker.finished.connect(self._end_refresh)
         self.worker.start()
+        if discovered:
+            message = trans._(
+                'When installing/uninstalling npe2 plugins, you must restart napari for UI changes to take effect.'
+            )
+            self._warn_dialog = WarnPopup(
+                text=message,
+            )
+            self._warn_dialog.exec_()
 
     def setup_ui(self):
         self.resize(1080, 640)
diff --git a/napari/_qt/widgets/qt_message_popup.py b/napari/_qt/widgets/qt_message_popup.py
--- a/napari/_qt/widgets/qt_message_popup.py
+++ b/napari/_qt/widgets/qt_message_popup.py
@@ -35,6 +35,7 @@ def __init__(
         self.setLayout(main_layout)
 
         self.setStyleSheet(get_stylesheet(get_settings().appearance.theme))
+        self._xbutton.raise_()
 
     def _close(self):
         self.close()


Test patch: