Repository: napari/napari

Issue:
Error when locking shape aspect ratio with shift 
## 🐛 Bug
haven't looked too far into yet, but just want to note it somewhere:
In `examples/mouse_drag_callback.py`, if I try to use the `Shift` key to constrain the aspect ratio of a shape, I get the following error in `hold_to_lock_aspect_ratio`:

```
Traceback (most recent call last):
  File "napdev/lib/python3.7/site-packages/vispy/app/backends/_qt.py", line 502, in keyPressEvent
    self._keyEvent(self._vispy_canvas.events.key_press, ev)
  File "napdev/lib/python3.7/site-packages/vispy/app/backends/_qt.py", line 551, in _keyEvent
    func(native=ev, key=key, text=text_type(ev.text()), modifiers=mod)
  File "napdev/lib/python3.7/site-packages/vispy/util/event.py", line 455, in __call__
    self._invoke_callback(cb, event)
  File "napdev/lib/python3.7/site-packages/vispy/util/event.py", line 475, in _invoke_callback
    self, cb_event=(cb, event))
  File "napdev/lib/python3.7/site-packages/vispy/util/event.py", line 471, in _invoke_callback
    cb(event)
  File "napari/napari/_qt/qt_viewer.py", line 394, in on_key_press
    next(gen)
  File "napari/napari/layers/shapes/keybindings.py", line 39, in hold_to_lock_aspect_ratio
    layer._move(layer.coordinates[layer.dims.displayed])
TypeError: tuple indices must be integers or slices, not list
```


Discussion:
I can work on a fix for this for `0.2.10`, should be a one-liner

Resolving patch:
diff --git a/napari/layers/shapes/keybindings.py b/napari/layers/shapes/keybindings.py
--- a/napari/layers/shapes/keybindings.py
+++ b/napari/layers/shapes/keybindings.py
@@ -36,14 +36,14 @@ def hold_to_lock_aspect_ratio(layer):
     else:
         layer._aspect_ratio = 1
     if layer._is_moving:
-        layer._move(layer.coordinates[layer.dims.displayed])
+        layer._move([layer.coordinates[i] for i in layer.dims.displayed])
 
     yield
 
     # on key release
     layer._fixed_aspect = False
     if layer._is_moving:
-        layer._move(layer.coordinates[layer.dims.displayed])
+        layer._move([layer.coordinates[i] for i in layer.dims.displayed])
 
 
 @Shapes.bind_key('R')


Test patch:
diff --git a/napari/layers/shapes/tests/test_shapes_keybindings.py b/napari/layers/shapes/tests/test_shapes_keybindings.py
new file mode 100644
--- /dev/null
+++ b/napari/layers/shapes/tests/test_shapes_keybindings.py
@@ -0,0 +1,113 @@
+import numpy as np
+from napari.layers import Shapes
+from napari.layers.shapes import keybindings
+
+
+def test_lock_aspect_ratio():
+    # Test a single four corner rectangle
+    layer = Shapes(20 * np.random.random((1, 4, 2)))
+    layer._is_moving = True
+    # need to go through the generator
+    _ = list(keybindings.hold_to_lock_aspect_ratio(layer))
+
+
+def test_lock_aspect_ratio_selected_box():
+    # Test a single four corner rectangle
+    layer = Shapes(20 * np.random.random((1, 4, 2)))
+    # select a shape
+    layer._selected_box = layer.interaction_box(0)
+    layer._is_moving = True
+    # need to go through the generator
+    _ = list(keybindings.hold_to_lock_aspect_ratio(layer))
+
+
+def test_lock_aspect_ratio_selected_box_zeros():
+    # Test a single four corner rectangle that has zero size
+    layer = Shapes(20 * np.zeros((1, 4, 2)))
+    # select a shape
+    layer._selected_box = layer.interaction_box(0)
+    layer._is_moving = True
+    # need to go through the generator
+    _ = list(keybindings.hold_to_lock_aspect_ratio(layer))
+
+
+def test_hold_to_pan_zoom():
+    # Test a single four corner rectangle
+    layer = Shapes(20 * np.random.random((1, 4, 2)))
+    layer.mode = 'direct'
+    # need to go through the generator
+    _ = list(keybindings.hold_to_pan_zoom(layer))
+
+
+def test_activate_modes():
+    # Test a single four corner rectangle
+    layer = Shapes(20 * np.random.random((1, 4, 2)))
+    # need to go through the generator
+    keybindings.activate_add_rectangle_mode(layer)
+    assert layer.mode == 'add_rectangle'
+    keybindings.activate_add_ellipse_mode(layer)
+    assert layer.mode == 'add_ellipse'
+    keybindings.activate_add_line_mode(layer)
+    assert layer.mode == 'add_line'
+    keybindings.activate_add_path_mode(layer)
+    assert layer.mode == 'add_path'
+    keybindings.activate_add_polygon_mode(layer)
+    assert layer.mode == 'add_polygon'
+    keybindings.activate_direct_mode(layer)
+    assert layer.mode == 'direct'
+    keybindings.activate_select_mode(layer)
+    assert layer.mode == 'select'
+    keybindings.activate_pan_zoom_mode(layer)
+    assert layer.mode == 'pan_zoom'
+    keybindings.activate_vertex_insert_mode(layer)
+    assert layer.mode == 'vertex_insert'
+    keybindings.activate_vertex_remove_mode(layer)
+    assert layer.mode == 'vertex_remove'
+
+
+def test_copy_paste():
+    # Test on three four corner rectangle
+    layer = Shapes(20 * np.random.random((3, 4, 2)))
+    layer.mode = 'direct'
+
+    assert len(layer.data) == 3
+    assert layer._clipboard == {}
+    layer.selected_data = [0, 1]
+
+    keybindings.copy(layer)
+    assert len(layer.data) == 3
+    assert len(layer._clipboard) == 2
+
+    keybindings.paste(layer)
+    assert len(layer.data) == 5
+    assert len(layer._clipboard) == 2
+
+
+def test_select_all():
+    # Test on three four corner rectangle
+    layer = Shapes(20 * np.random.random((3, 4, 2)))
+    layer.mode = 'direct'
+
+    assert len(layer.data) == 3
+    assert len(layer.selected_data) == 0
+
+    keybindings.select_all(layer)
+    assert len(layer.selected_data) == 3
+
+
+def test_delete():
+    # Test on three four corner rectangle
+    layer = Shapes(20 * np.random.random((3, 4, 2)))
+    layer.mode = 'direct'
+
+    assert len(layer.data) == 3
+    layer.selected_data = [0, 1]
+
+    keybindings.delete_selected(layer)
+    assert len(layer.data) == 1
+
+
+def test_finish():
+    # Test on three four corner rectangle
+    layer = Shapes(20 * np.random.random((3, 4, 2)))
+    keybindings.finish_drawing(layer)