Repository: napari/napari

Issue:
multiple_viewer_widget.py example is broken
## 🐛 Bug

The [`examples/multiple_viewer_widget.py`](https://github.com/napari/napari/blob/main/examples/multiple_viewer_widget.py) script is broken on the main branch.

## To Reproduce

Steps to reproduce the behavior:

1. `python examples/multiple_viewer_widget.py`
2. Open Sample -> napari builtins -> Cells

<!-- If you have a code sample, error messages, stack traces, please provide it here as well -->

It's leads to a `RecursionError`. Here is what is repeated in my stack trace:
```
File "/home/plumail/code/nap/napari/examples/multiple_viewer_widget.py", line 329, in _point_update
    model.dims.current_step = event.value
  File "/home/plumail/code/nap/napari/napari/utils/events/evented_model.py", line 283, in __setattr__
    self._super_setattr_(name, value)
  File "/home/plumail/code/nap/napari/napari/utils/events/evented_model.py", line 263, in _super_setattr_
    setter(self, value)
  File "/home/plumail/code/nap/napari/napari/components/dims.py", line 247, in current_step
    self.point = [
  File "/home/plumail/code/nap/napari/napari/utils/events/evented_model.py", line 305, in __setattr__
    getattr(self.events, dep)(value=after_deps[dep])
  File "/home/plumail/code/nap/napari/napari/utils/events/event.py", line 763, in __call__
    self._invoke_callback(cb, event if pass_event else None)
  File "/home/plumail/code/nap/napari/napari/utils/events/event.py", line 801, in _invoke_callback
    _handle_exception(
  File "/home/plumail/code/nap/napari/napari/utils/events/event.py", line 788, in _invoke_callback
    cb(event)
  File "/home/plumail/code/nap/napari/examples/multiple_viewer_widget.py", line 329, in _point_update
    model.dims.current_step = event.value
```

## Expected behavior

<!-- A clear and concise description of what you expected to happen. -->

It should open the image.

## Environment

 - Please copy and paste the information at napari info option in help menubar here:
```
napari: 0.4.17rc4.dev233+g1f67a828.d20230424
Platform: Linux-5.14.0-1057-oem-x86_64-with-glibc2.36
System: Ubuntu 22.10
Python: 3.9.16 | packaged by conda-forge | (main, Feb 1 2023, 21:39:03) [GCC 11.3.0]
Qt: 5.15.2
PyQt5: 5.15.9
NumPy: 1.24.2
SciPy: 1.9.1
Dask: 2023.3.2
VisPy: 0.12.2
magicgui: 0.7.2
superqt: unknown
in-n-out: 0.1.7
app-model: 0.1.2
npe2: 0.6.2

OpenGL:
- GL version: 4.6.0 NVIDIA 530.30.02
- MAX_TEXTURE_SIZE: 32768

Screens:
- screen 1: resolution 2560x1440, scale 1.0

Settings path:
- /home/plumail/.config/napari/napari-stable_f9b3d8c4ec78b985009865efe6681470d52609c3/settings.yaml
```
 - Any other relevant information:
It works fine on the 0.4.17 branch.

## Additional context

<!-- Add any other context about the problem here. -->



Discussion:
Thanks for reporting, I suspect this is due to recent work on points and also the canvas. This example was possibly overlooked.

Resolving patch:
diff --git a/examples/multiple_viewer_widget.py b/examples/multiple_viewer_widget.py
--- a/examples/multiple_viewer_widget.py
+++ b/examples/multiple_viewer_widget.py
@@ -326,6 +326,8 @@ def _point_update(self, event):
         for model in [self.viewer, self.viewer_model1, self.viewer_model2]:
             if model.dims is event.source:
                 continue
+            if len(self.viewer.layers) != len(model.layers):
+                continue
             model.dims.current_step = event.value
 
     def _order_update(self):
@@ -457,4 +459,6 @@ def _property_sync(self, name, event):
     view.window.add_dock_widget(dock_widget, name="Sample")
     view.window.add_dock_widget(cross, name="Cross", area="left")
 
+    view.open_sample('napari', 'cells3d')
+
     napari.run()
diff --git a/napari/components/dims.py b/napari/components/dims.py
--- a/napari/components/dims.py
+++ b/napari/components/dims.py
@@ -182,8 +182,8 @@ def _check_dims(cls, values):
         # order and label default computation is too different to include in ensure_len()
         # Check the order tuple has same number of elements as ndim
         order = values['order']
-        order_ndim = len(order)
         if len(order) < ndim:
+            order_ndim = len(order)
             # new dims are always prepended
             prepended_dims = tuple(range(ndim - order_ndim))
             # maintain existing order, but shift accordingly
@@ -194,7 +194,7 @@ def _check_dims(cls, values):
         updated['order'] = order
 
         # Check the order is a permutation of 0, ..., ndim - 1
-        if not set(updated['order']) == set(range(ndim)):
+        if set(updated['order']) != set(range(ndim)):
             raise ValueError(
                 trans._(
                     "Invalid ordering {order} for {ndim} dimensions",
@@ -230,10 +230,10 @@ def nsteps(self) -> Tuple[float, ...]:
 
     @nsteps.setter
     def nsteps(self, value):
-        self.range = [
-            (rng.start, rng.stop, (rng.stop - rng.start) / nsteps)
+        self.range = tuple(
+            RangeTuple(rng.start, rng.stop, (rng.stop - rng.start) / nsteps)
             for rng, nsteps in zip(self.range, value)
-        ]
+        )
 
     @property
     def current_step(self):
@@ -244,10 +244,10 @@ def current_step(self):
 
     @current_step.setter
     def current_step(self, value):
-        self.point = [
+        self.point = tuple(
             rng.start + point * rng.step
             for point, rng in zip(value, self.range)
-        ]
+        )
 
     @property
     def thickness(self) -> Tuple[float, ...]:
@@ -297,7 +297,7 @@ def set_range(
         full_range = list(self.range)
         for ax, val in zip(axis, value):
             full_range[ax] = val
-        self.range = full_range
+        self.range = tuple(full_range)
 
     def set_point(
         self,
@@ -319,7 +319,7 @@ def set_point(
         full_point = list(self.point)
         for ax, val in zip(axis, value):
             full_point[ax] = val
-        self.point = full_point
+        self.point = tuple(full_point)
 
     def set_current_step(
         self,


Test patch: