Repository: napari/napari

Issue:
Connecting to external monitors breaks pan/ zoom
## 🐛 Bug

I've noticed a couple times now that when napari is already open and you connect to an external monitor the vispy canvas get's all confused with its zoom / camera / panning.

I'm not sure if you still get the same effects if you open napari after you've already connected to an external monitor. I will investigate.

Vispy has already tried to fix some of these issues - see this here https://github.com/vispy/vispy/pull/1598/files
but we might need to look into them too on napari, as it's not so great for giving live demos during talks ;-)

@jni you might remember this happened during might lightning talk at SciPy


Discussion:
Oh no! This happened again at Bioimage Informatics???

fwiw, on Linux, when launching napari *after* connecting my external monitor, everything works fine. I'll try doing it before to see what happens. Also, I am typically not using mirroring, but based on that vispy PR, it looks like the issue is about resolution rather than position.
It actually didn’t - demo was fine because I waited to launch until after connecting, but we should try and fix the other behavior too
@sofroniewn Whew! 😅 Are we sure that vispy master hasn't made the issue magically go away on our end?
Unfortunately this issue still occurs for me. It's worst when mirroring displays, see this gif of panning/ zooming the astronaut and notice the weird jumps and cropping

![mirror_displays](https://user-images.githubusercontent.com/6531703/95037368-a132b400-067f-11eb-8614-6ae09d8ffb16.gif)

We'll likely want to keep looking into one more.
some progress: if you see the weirdness after you switch monitors, etc, you can get it to work normally again on your monitor by adjusting the napari window size. I am looking into what is refreshed in that now
a little more progress: I traced down and found the physical_size and pixel_scale on canvas are being drastically changed when moving across monitors, but I have yet to find a way to fire off signals to have the canvas update those on screen change
> a little more progress: I traced down and found the physical_size and pixel_scale on canvas are being drastically changed when moving across monitors, but I have yet to find a way to fire off signals to have the canvas update those on screen change

ooo this sounds very promising!
alright I think I know what is going on:
We initialize the viewer and canvas before the main window, see https://github.com/napari/napari/blob/b61d9ae570e30091a97b6c76e37cd95fe5b296b6/napari/viewer.py#L110-L111

however, during canvas init, if it doesn't have reference to window, it would not wire the screen change signal to refresh: https://github.com/vispy/vispy/blob/b6cbf11bd506d48c435c6e01e36e89fc54a16d1e/vispy/app/backends/_qt.py#L349-L354

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
@@ -69,6 +69,12 @@ def __init__(self, qt_viewer: QtViewer, *, show: bool = True):
         self._qt_window = QMainWindow()
         self._qt_window.setAttribute(Qt.WA_DeleteOnClose)
         self._qt_window.setUnifiedTitleAndToolBarOnMac(True)
+
+        # since we initialize canvas before window, we need to manually connect them again.
+        if self._qt_window.windowHandle() is not None:
+            self._qt_window.windowHandle().screenChanged.connect(
+                self.qt_viewer.canvas._backend.screen_changed
+            )
         self._qt_center = QWidget(self._qt_window)
 
         self._qt_window.setCentralWidget(self._qt_center)


Test patch: