Repository: napari/napari

Issue:
Grid mode auto-zoom should be improved
## 🚀 Feature

When using grid view with just two layers, the zoom is set as if I was looking at a 4x4 grid. imho it should be a bit smarter and zoom in further. Here's a small example to reproduce:

```
import numpy as np
import napari


n = 28
image = np.random.random((2, n, n))

with napari.gui_qt():
    viewer = napari.view_image(image, channel_axis=0)
    viewer.grid_view()
```

This looks like this:

<img width="1200" alt="Screen Shot 2020-10-04 at 1 21 59 pm" src="https://user-images.githubusercontent.com/492549/95005626-ef779100-0646-11eb-9904-1e9fdd6009c9.png">

Whereas I think it should look like this:

<img width="1200" alt="Screen Shot 2020-10-04 at 1 22 16 pm" src="https://user-images.githubusercontent.com/492549/95005638-f9998f80-0646-11eb-8f44-9f3865cffb87.png">


Discussion:
I can fix this when I get back to the camera PR #854, thanks for the flag!! All this stuff is becoming much much easier to reason about with the merge of #1660 and things like #1673 getting close!!
Actually I might wait till after #854 to fix it in its own PR where I clean up grid mode, that's actually been long overdue. Regardless this should get fixed soon, and the screenshots are very helpful!!
Speaking of auto-zoom, should we have this happen every time a new layer is added? And does auto-zoom take into account all of the layers, or all of the visible layers, or just the last selected layer?

If I add an image with a larger field of view after one with a smaller field of view, then parts of the bigger image appear cut off in the image viewer. It's not always super-obvious the user just needs to zoom in or out, if the cut off part isn't a lot I can wind up wondering what's gone wrong with my data.


Ok I'm looking at this again. Funnily enough the discussion happening at the tail end of https://github.com/napari/napari/pull/854#issuecomment-713843658 is highly relevant here too due to the non-square aspect ratio of the grid example above. I can try and improve things, but wanted to link there too.


> Speaking of auto-zoom, should we have this happen every time a new layer is added? And does auto-zoom take into account all of the layers, or all of the visible layers, or just the last selected layer?

- Right reset view does not happen automatically when a layer adds
- Reset view takes into account all layers, including non-visible and non-selected

> If I add an image with a larger field of view after one with a smaller field of view, then parts of the bigger image appear cut off in the image viewer. It's not always super-obvious the user just needs to zoom in or out, if the cut off part isn't a lot I can wind up wondering what's gone wrong with my data.

Hmm ok, yeah we might want to reset the view when people add new layers, might be more natural.

Resolving patch:
diff --git a/napari/components/viewer_model.py b/napari/components/viewer_model.py
--- a/napari/components/viewer_model.py
+++ b/napari/components/viewer_model.py
@@ -434,8 +434,8 @@ def grid_view(self, n_row=None, n_column=None, stride=1):
         """
         n_grid_squares = np.ceil(len(self.layers) / abs(stride)).astype(int)
         if n_row is None and n_column is None:
-            n_row = np.ceil(np.sqrt(n_grid_squares)).astype(int)
-            n_column = n_row
+            n_column = np.ceil(np.sqrt(n_grid_squares)).astype(int)
+            n_row = np.ceil(n_grid_squares / n_column).astype(int)
         elif n_row is None:
             n_row = np.ceil(n_grid_squares / n_column).astype(int)
         elif n_column is None:


Test patch:
diff --git a/napari/_tests/test_with_screenshot.py b/napari/_tests/test_with_screenshot.py
--- a/napari/_tests/test_with_screenshot.py
+++ b/napari/_tests/test_with_screenshot.py
@@ -256,7 +256,7 @@ def test_grid_mode(make_test_viewer):
 
     # enter grid view
     viewer.grid_view()
-    assert np.all(viewer.grid_size == (3, 3))
+    assert np.all(viewer.grid_size == (2, 3))
     assert viewer.grid_stride == 1
     translations = [layer.translate_grid for layer in viewer.layers]
     expected_translations = [
@@ -276,9 +276,9 @@ def test_grid_mode(make_test_viewer):
         (1 / 3, 1 / 3),
         (1 / 3, 1 / 2),
         (1 / 3, 2 / 3),
-        (1 / 2, 1 / 3),
-        (1 / 2, 1 / 2),
-        (1 / 2, 2 / 3),
+        (2 / 3, 1 / 3),
+        (2 / 3, 1 / 2),
+        (2 / 3, 2 / 3),
     ]
     # BGRMYC color order
     color = [
@@ -300,15 +300,6 @@ def test_grid_mode(make_test_viewer):
 
     # check screenshot
     screenshot = viewer.screenshot(canvas_only=True)
-    # sample 6 squares of the grid and check they have right colors
-    pos = [
-        (1 / 3, 1 / 3),
-        (1 / 3, 1 / 2),
-        (1 / 3, 2 / 3),
-        (1 / 2, 1 / 3),
-        (1 / 2, 1 / 2),
-        (1 / 2, 2 / 3),
-    ]
     # CGRMYB color order
     color = [
         [0, 255, 255, 255],
diff --git a/napari/components/_tests/test_viewer_model.py b/napari/components/_tests/test_viewer_model.py
--- a/napari/components/_tests/test_viewer_model.py
+++ b/napari/components/_tests/test_viewer_model.py
@@ -339,7 +339,7 @@ def test_grid():
 
     # enter grid view
     viewer.grid_view()
-    assert np.all(viewer.grid_size == (3, 3))
+    assert np.all(viewer.grid_size == (2, 3))
     assert viewer.grid_stride == 1
     translations = [layer.translate_grid for layer in viewer.layers]
     expected_translations = [