Repository: napari/napari

Issue:
New shape properties added to layer.properties?
## 🐛 Bug

I am trying to use napari as a basic ROI management tool to view and edit ROIs on an image. However, the expected behavior of the layer.properties attributes when new shapes are added to an existing shape layer is unclear to me. When no properties are passed to viewer.add_layer() then no properties are added when new shapes are added. If there is a dictionary of properties, then a new shape adds not 1 but 4 entries into the the dict value list in the properties dict, where all four entries are copies of the last entry in the list.

## To Reproduce

Steps to reproduce the behavior:


```
test_shapes = [np.array([[100,200],[200,300]]), np.array([[300,400], [400, 500]])]
test_properties = {'index': [1,2]}

with napari.gui_qt():
    viewer = napari.view_image(test_img)
    layer = viewer.add_shapes(test_shapes, properties=test_properties)
```
I then add a new shape to the shapes layer in the napari gui and then check the layer object:

```
layer.data

[array([[100., 200.],
        [200., 200.],
        [200., 300.],
        [100., 300.]]),
 array([[300., 400.],
        [400., 400.],
        [400., 500.],
        [300., 500.]]),
 array([[613.323895  , 741.44912242],
        [707.00687235, 741.44912242],
        [707.00687235, 939.83660388],
        [613.323895  , 939.83660388]])]

layer.properties

{'index': array([1, 2, 2, 2, 2, 2])}
```

## Expected behavior

I understand that it is hard to add reasonable values to an arbitrary properties list which could be anything basically, and I'm not completely sure what to suggest here. But at the very least adding one shape should add one property, not four, and if the property is a list of ints autoincrementation could be a possibility?

## Environment

napari: 0.3.5
Platform: Windows-10-10.0.19041-SP0
Python: 3.7.6 | packaged by conda-forge | (default, Jun 1 2020, 18:11:50) [MSC v.1916 64 bit (AMD64)]
Qt: 5.12.5
PyQt5: 5.12.3
NumPy: 1.17.0
SciPy: 1.5.0
Dask: 2.19.0
VisPy: 0.6.4

GL version: 4.6.0 NVIDIA 391.25
MAX_TEXTURE_SIZE: 16384

Plugins:
- napari-plugin-engine: 0.1.6
- svg: 0.1.3



Discussion:
Hello @KaSaBe ! Thank you for reporting this bug! This is certainly not the intended behavior. As you guessed, only one property should be added for each new shape. The intention was that it would be the value of the last selected shape unless otherwise specified. I will be spending a good chunk of time on `napari` this weekend for the scipy sprints and I will look at this again. Thanks again for the report and sorry for the inconvenience.

Resolving patch:
diff --git a/napari/layers/shapes/shapes.py b/napari/layers/shapes/shapes.py
--- a/napari/layers/shapes/shapes.py
+++ b/napari/layers/shapes/shapes.py
@@ -1365,7 +1365,11 @@ def add(
         if edge_width is None:
             edge_width = self.current_edge_width
 
-        n_new_shapes = len(data)
+        if np.array(data[0]).ndim == 1:
+            # If a single array for a shape
+            n_new_shapes = 1
+        else:
+            n_new_shapes = len(data)
 
         if edge_color is None:
             edge_color = self._get_new_shape_color(


Test patch:
diff --git a/napari/layers/shapes/_tests/test_shapes.py b/napari/layers/shapes/_tests/test_shapes.py
--- a/napari/layers/shapes/_tests/test_shapes.py
+++ b/napari/layers/shapes/_tests/test_shapes.py
@@ -1286,3 +1286,41 @@ def test_to_labels_3D():
     labels = layer.to_labels(labels_shape=labels_shape)
     assert np.all(labels.shape == labels_shape)
     assert np.all(np.unique(labels) == [0, 1, 2, 3])
+
+
+def test_add_single_shape_consistent_properties():
+    """Test adding a single shape ensures correct number of added properties"""
+    data = [
+        np.array([[100, 200], [200, 300]]),
+        np.array([[300, 400], [400, 500]]),
+    ]
+    properties = {'index': [1, 2]}
+    layer = Shapes(
+        np.array(data), shape_type='rectangle', properties=properties
+    )
+
+    layer.add(np.array([[500, 600], [700, 800]]))
+    assert len(layer.properties['index']) == 3
+    assert layer.properties['index'][2] == 2
+
+
+def test_add_shapes_consistent_properties():
+    """Test adding multiple shapes ensures correct number of added properties"""
+    data = [
+        np.array([[100, 200], [200, 300]]),
+        np.array([[300, 400], [400, 500]]),
+    ]
+    properties = {'index': [1, 2]}
+    layer = Shapes(
+        np.array(data), shape_type='rectangle', properties=properties
+    )
+
+    layer.add(
+        [
+            np.array([[500, 600], [700, 800]]),
+            np.array([[700, 800], [800, 900]]),
+        ]
+    )
+    assert len(layer.properties['index']) == 4
+    assert layer.properties['index'][2] == 2
+    assert layer.properties['index'][3] == 2