We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Massive traceback when deleting a (markers) layer
## 🐛 Bug

Sorry, no time to investigate now, but here is the traceback I saw when deleting a markers layer (using the trash icon):

<details>

```pytb
In [15]: WARNING: Traceback (most recent call last):
  File "/Users/jni/conda/envs/36/bin/ipython", line 11, in <module>
    sys.exit(start_ipython())
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/__init__.py", line 125, in start_ipython
    return launch_new_instance(argv=argv, **kwargs)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/traitlets/config/application.py", line 658, in launch_instance
    app.start()
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/ipapp.py", line 356, in start
    self.shell.mainloop()
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/interactiveshell.py", line 480, in mainloop
    self.interact()
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/interactiveshell.py", line 463, in interact
    code = self.prompt_for_code()
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/interactiveshell.py", line 376, in prompt_for_code
    pre_run=self.pre_prompt, reset_current_buffer=True)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/prompt_toolkit/interface.py", line 415, in run
    self.eventloop.run(self.input, self.create_eventloop_callbacks())
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/prompt_toolkit/eventloop/posix.py", line 102, in run
    self._inputhook_context.call_inputhook(ready)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/prompt_toolkit/eventloop/inputhook.py", line 74, in call_inputhook
    self.inputhook(self)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/interactiveshell.py", line 495, in inputhook
    self._inputhook(context)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/pt_inputhooks/qt.py", line 35, in inputhook
    event_loop.exec_()
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/components/_layers_list/model.py", line 182, in remove_selected
    self.pop(i)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/util/list/_model.py", line 55, in pop
    self.changed.removed(item=obj)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/util/event.py", line 489, in __call__
    self._invoke_callback(cb, event)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/util/event.py", line 504, in _invoke_callback
    cb(event)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/components/_layers_list/model.py", line 29, in _remove
    layer.viewer = None
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/layers/_base_layer/model.py", line 162, in viewer
    self._parent = parent
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/layers/_base_layer/_visual_wrapper.py", line 82, in _parent
    self._node.parent = parent
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/frozen.py", line 17, in __setattr__
    object.__setattr__(self, key, value)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/scene/node.py", line 209, in parent
    self._update_trsys(None)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/_vispy/scene/visuals.py", line 82, in _update_trsys
    self.transforms.scene_transform = scene.node_transform(doc)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/transform_system.py", line 276, in scene_transform
    self._scene_transform.transforms = tr
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/chain.py", line 96, in transforms
    self.update()
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/base_transform.py", line 153, in update
    self.changed(*args)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/event.py", line 455, in __call__
    self._invoke_callback(cb, event)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/event.py", line 471, in _invoke_callback
    cb(event)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/chain.py", line 212, in _subtr_changed
    self.update(ev)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/base_transform.py", line 153, in update
    self.changed(*args)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/event.py", line 455, in __call__
    self._invoke_callback(cb, event)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/event.py", line 475, in _invoke_callback
    self, cb_event=(cb, event))
  << caught exception here: >>
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/event.py", line 471, in _invoke_callback
    cb(event)
  File "/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/chain.py", line 281, in source_changed
    new_tr = [tr[0]]
IndexError: list index out of range
ERROR: Invoking <bound method SimplifiedChainTransform.source_changed of <ChainTransform [<STTransform scale=[1. 1. 1. 1.] translate=[0. 0. 0. 0.] at 0x5071730616>,
                 <STTransform scale=[2.6181054e+00 2.6181054e+00 1.0000000e-06 1.0000000e+00] translate=[-558.83954 -403.84387    0.         0.     ] at 0x5319047544>] at 0x12e27aba8>> for Event
```

</details>

## To Reproduce

Approximately: (not sure which bits are important, maybe only the final step matters)
- add a (3D) image
- add a second image (segmentation in this case)
- decrease opacity of second image
- add a markers layer (with the + button)
- set annotation mode
- pan and zoom while holding down space
- hit the delete button.

## Expected behavior

Console should stay nice and clean when layers are deleted. ;)

## Environment

 - napari Version (e.g., 1.0): 0.0.6 (from PyPI)
 - OS (e.g., Linux): macOS Mojave
 - Python version: 3.6
 - Any other relevant information:


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `napari/components/layerlist.py`
Content:
```
1 from ..layers import Layer
2 
3 from ..util.naming import inc_name_count
4 from ..util.list import ListModel
5 
6 
7 def _add(event):
8     """When a layer is added, set its name and order."""
9     layers = event.source
10     layer = event.item
11     layer.name = layers._coerce_name(layer.name, layer)
12     layer._order = -len(layers)
13     layer.events.name.connect(lambda e: layers._update_name(e))
14     layers.unselect_all(ignore=layer)
15 
16 
17 def _remove(event):
18     """When a layer is removed, remove its viewer."""
19     layers = event.source
20     layer = event.item
21     layer._order = 0
22     layer._node.parent = None
23 
24 
25 def _reorder(event):
26     """When the list is reordered, propagate those changes to draw order."""
27     layers = event.source
28     for i in range(len(layers)):
29         layers[i]._order = -i
30 
31 
32 class LayerList(ListModel):
33     """List-like layer collection with built-in reordering and callback hooks.
34 
35     Attributes
36     ----------
37     events : vispy.util.event.EmitterGroup
38         Event hooks:
39             * added(item, index): whenever an item is added
40             * removed(item): whenever an item is removed
41             * reordered(): whenever the list is reordered
42     """
43 
44     def __init__(self):
45         super().__init__(
46             basetype=Layer, lookup={str: lambda q, e: q == e.name}
47         )
48 
49         self.events.added.connect(_add)
50         self.events.removed.connect(_remove)
51         self.events.reordered.connect(_reorder)
52 
53     def __newlike__(self, iterable):
54         return ListModel(self._basetype, iterable, self._lookup)
55 
56     def _coerce_name(self, name, layer=None):
57         """Coerce a name into a unique equivalent.
58 
59         Parameters
60         ----------
61         name : str
62             Original name.
63         layer : Layer, optional
64             Layer for which name is generated.
65 
66         Returns
67         -------
68         new_name : str
69             Coerced, unique name.
70         """
71         for l in self:
72             if l is layer:
73                 continue
74             if l.name == name:
75                 name = inc_name_count(name)
76 
77         return name
78 
79     def _update_name(self, event):
80         """Coerce name of the layer in `event.layer`."""
81         layer = event.source
82         layer.name = self._coerce_name(layer.name, layer)
83 
84     def move_selected(self, index, insert):
85         """Reorder list by moving the item at index and inserting it
86         at the insert index. If additional items are selected these will
87         get inserted at the insert index too. This allows for rearranging
88         the list based on dragging and dropping a selection of items, where
89         index is the index of the primary item being dragged, and insert is
90         the index of the drop location, and the selection indicates if
91         multiple items are being dragged. If the moved layer is not selected
92         select it.
93 
94         Parameters
95         ----------
96         index : int
97             Index of primary item to be moved
98         insert : int
99             Index that item(s) will be inserted at
100         """
101         total = len(self)
102         indices = list(range(total))
103         if not self[index].selected:
104             self.unselect_all()
105             self[index].selected = True
106         selected = [i for i in range(total) if self[i].selected]
107 
108         # remove all indices to be moved
109         for i in selected:
110             indices.remove(i)
111         # adjust offset based on selected indices to move
112         offset = sum([i < insert and i != index for i in selected])
113         # insert indices to be moved at correct start
114         for insert_idx, elem_idx in enumerate(selected, start=insert - offset):
115             indices.insert(insert_idx, elem_idx)
116         # reorder list
117         self[:] = self[tuple(indices)]
118 
119     def unselect_all(self, ignore=None):
120         """Unselects all layers expect any specified in ignore.
121 
122         Parameters
123         ----------
124         ignore : Layer | None
125             Layer that should not be unselected if specified.
126         """
127         for layer in self:
128             if layer.selected and layer != ignore:
129                 layer.selected = False
130 
131     def remove_selected(self):
132         """Removes selected items from list.
133         """
134         to_delete = []
135         for i in range(len(self)):
136             if self[i].selected:
137                 to_delete.append(i)
138         to_delete.reverse()
139         for i in to_delete:
140             self.pop(i)
141
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/napari/components/layerlist.py b/napari/components/layerlist.py
--- a/napari/components/layerlist.py
+++ b/napari/components/layerlist.py
@@ -2,6 +2,7 @@
 
 from ..util.naming import inc_name_count
 from ..util.list import ListModel
+from vispy.visuals.transforms import ChainTransform
 
 
 def _add(event):
@@ -19,6 +20,7 @@
     layers = event.source
     layer = event.item
     layer._order = 0
+    layer._node.transforms = ChainTransform()
     layer._node.parent = None

{"golden_diff": "diff --git a/napari/components/layerlist.py b/napari/components/layerlist.py\n--- a/napari/components/layerlist.py\n+++ b/napari/components/layerlist.py\n@@ -2,6 +2,7 @@\n \n from ..util.naming import inc_name_count\n from ..util.list import ListModel\n+from vispy.visuals.transforms import ChainTransform\n \n \n def _add(event):\n@@ -19,6 +20,7 @@\n     layers = event.source\n     layer = event.item\n     layer._order = 0\n+    layer._node.transforms = ChainTransform()\n     layer._node.parent = None\n", "issue": "Massive traceback when deleting a (markers) layer\n## \ud83d\udc1b Bug\r\n\r\nSorry, no time to investigate now, but here is the traceback I saw when deleting a markers layer (using the trash icon):\r\n\r\n<details>\r\n\r\n```pytb\r\nIn [15]: WARNING: Traceback (most recent call last):\r\n  File \"/Users/jni/conda/envs/36/bin/ipython\", line 11, in <module>\r\n    sys.exit(start_ipython())\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/__init__.py\", line 125, in start_ipython\r\n    return launch_new_instance(argv=argv, **kwargs)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/traitlets/config/application.py\", line 658, in launch_instance\r\n    app.start()\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/ipapp.py\", line 356, in start\r\n    self.shell.mainloop()\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/interactiveshell.py\", line 480, in mainloop\r\n    self.interact()\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/interactiveshell.py\", line 463, in interact\r\n    code = self.prompt_for_code()\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/interactiveshell.py\", line 376, in prompt_for_code\r\n    pre_run=self.pre_prompt, reset_current_buffer=True)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/prompt_toolkit/interface.py\", line 415, in run\r\n    self.eventloop.run(self.input, self.create_eventloop_callbacks())\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/prompt_toolkit/eventloop/posix.py\", line 102, in run\r\n    self._inputhook_context.call_inputhook(ready)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/prompt_toolkit/eventloop/inputhook.py\", line 74, in call_inputhook\r\n    self.inputhook(self)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/interactiveshell.py\", line 495, in inputhook\r\n    self._inputhook(context)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/IPython/terminal/pt_inputhooks/qt.py\", line 35, in inputhook\r\n    event_loop.exec_()\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/components/_layers_list/model.py\", line 182, in remove_selected\r\n    self.pop(i)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/util/list/_model.py\", line 55, in pop\r\n    self.changed.removed(item=obj)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/util/event.py\", line 489, in __call__\r\n    self._invoke_callback(cb, event)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/util/event.py\", line 504, in _invoke_callback\r\n    cb(event)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/components/_layers_list/model.py\", line 29, in _remove\r\n    layer.viewer = None\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/layers/_base_layer/model.py\", line 162, in viewer\r\n    self._parent = parent\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/layers/_base_layer/_visual_wrapper.py\", line 82, in _parent\r\n    self._node.parent = parent\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/frozen.py\", line 17, in __setattr__\r\n    object.__setattr__(self, key, value)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/scene/node.py\", line 209, in parent\r\n    self._update_trsys(None)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/napari/_vispy/scene/visuals.py\", line 82, in _update_trsys\r\n    self.transforms.scene_transform = scene.node_transform(doc)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/transform_system.py\", line 276, in scene_transform\r\n    self._scene_transform.transforms = tr\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/chain.py\", line 96, in transforms\r\n    self.update()\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/base_transform.py\", line 153, in update\r\n    self.changed(*args)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/event.py\", line 455, in __call__\r\n    self._invoke_callback(cb, event)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/event.py\", line 471, in _invoke_callback\r\n    cb(event)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/chain.py\", line 212, in _subtr_changed\r\n    self.update(ev)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/base_transform.py\", line 153, in update\r\n    self.changed(*args)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/event.py\", line 455, in __call__\r\n    self._invoke_callback(cb, event)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/event.py\", line 475, in _invoke_callback\r\n    self, cb_event=(cb, event))\r\n  << caught exception here: >>\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/util/event.py\", line 471, in _invoke_callback\r\n    cb(event)\r\n  File \"/Users/jni/conda/envs/36/lib/python3.6/site-packages/vispy/visuals/transforms/chain.py\", line 281, in source_changed\r\n    new_tr = [tr[0]]\r\nIndexError: list index out of range\r\nERROR: Invoking <bound method SimplifiedChainTransform.source_changed of <ChainTransform [<STTransform scale=[1. 1. 1. 1.] translate=[0. 0. 0. 0.] at 0x5071730616>,\r\n                 <STTransform scale=[2.6181054e+00 2.6181054e+00 1.0000000e-06 1.0000000e+00] translate=[-558.83954 -403.84387    0.         0.     ] at 0x5319047544>] at 0x12e27aba8>> for Event\r\n```\r\n\r\n</details>\r\n\r\n## To Reproduce\r\n\r\nApproximately: (not sure which bits are important, maybe only the final step matters)\r\n- add a (3D) image\r\n- add a second image (segmentation in this case)\r\n- decrease opacity of second image\r\n- add a markers layer (with the + button)\r\n- set annotation mode\r\n- pan and zoom while holding down space\r\n- hit the delete button.\r\n\r\n## Expected behavior\r\n\r\nConsole should stay nice and clean when layers are deleted. ;)\r\n\r\n## Environment\r\n\r\n - napari Version (e.g., 1.0): 0.0.6 (from PyPI)\r\n - OS (e.g., Linux): macOS Mojave\r\n - Python version: 3.6\r\n - Any other relevant information:\r\n\n", "before_files": [{"content": "from ..layers import Layer\n\nfrom ..util.naming import inc_name_count\nfrom ..util.list import ListModel\n\n\ndef _add(event):\n    \"\"\"When a layer is added, set its name and order.\"\"\"\n    layers = event.source\n    layer = event.item\n    layer.name = layers._coerce_name(layer.name, layer)\n    layer._order = -len(layers)\n    layer.events.name.connect(lambda e: layers._update_name(e))\n    layers.unselect_all(ignore=layer)\n\n\ndef _remove(event):\n    \"\"\"When a layer is removed, remove its viewer.\"\"\"\n    layers = event.source\n    layer = event.item\n    layer._order = 0\n    layer._node.parent = None\n\n\ndef _reorder(event):\n    \"\"\"When the list is reordered, propagate those changes to draw order.\"\"\"\n    layers = event.source\n    for i in range(len(layers)):\n        layers[i]._order = -i\n\n\nclass LayerList(ListModel):\n    \"\"\"List-like layer collection with built-in reordering and callback hooks.\n\n    Attributes\n    ----------\n    events : vispy.util.event.EmitterGroup\n        Event hooks:\n            * added(item, index): whenever an item is added\n            * removed(item): whenever an item is removed\n            * reordered(): whenever the list is reordered\n    \"\"\"\n\n    def __init__(self):\n        super().__init__(\n            basetype=Layer, lookup={str: lambda q, e: q == e.name}\n        )\n\n        self.events.added.connect(_add)\n        self.events.removed.connect(_remove)\n        self.events.reordered.connect(_reorder)\n\n    def __newlike__(self, iterable):\n        return ListModel(self._basetype, iterable, self._lookup)\n\n    def _coerce_name(self, name, layer=None):\n        \"\"\"Coerce a name into a unique equivalent.\n\n        Parameters\n        ----------\n        name : str\n            Original name.\n        layer : Layer, optional\n            Layer for which name is generated.\n\n        Returns\n        -------\n        new_name : str\n            Coerced, unique name.\n        \"\"\"\n        for l in self:\n            if l is layer:\n                continue\n            if l.name == name:\n                name = inc_name_count(name)\n\n        return name\n\n    def _update_name(self, event):\n        \"\"\"Coerce name of the layer in `event.layer`.\"\"\"\n        layer = event.source\n        layer.name = self._coerce_name(layer.name, layer)\n\n    def move_selected(self, index, insert):\n        \"\"\"Reorder list by moving the item at index and inserting it\n        at the insert index. If additional items are selected these will\n        get inserted at the insert index too. This allows for rearranging\n        the list based on dragging and dropping a selection of items, where\n        index is the index of the primary item being dragged, and insert is\n        the index of the drop location, and the selection indicates if\n        multiple items are being dragged. If the moved layer is not selected\n        select it.\n\n        Parameters\n        ----------\n        index : int\n            Index of primary item to be moved\n        insert : int\n            Index that item(s) will be inserted at\n        \"\"\"\n        total = len(self)\n        indices = list(range(total))\n        if not self[index].selected:\n            self.unselect_all()\n            self[index].selected = True\n        selected = [i for i in range(total) if self[i].selected]\n\n        # remove all indices to be moved\n        for i in selected:\n            indices.remove(i)\n        # adjust offset based on selected indices to move\n        offset = sum([i < insert and i != index for i in selected])\n        # insert indices to be moved at correct start\n        for insert_idx, elem_idx in enumerate(selected, start=insert - offset):\n            indices.insert(insert_idx, elem_idx)\n        # reorder list\n        self[:] = self[tuple(indices)]\n\n    def unselect_all(self, ignore=None):\n        \"\"\"Unselects all layers expect any specified in ignore.\n\n        Parameters\n        ----------\n        ignore : Layer | None\n            Layer that should not be unselected if specified.\n        \"\"\"\n        for layer in self:\n            if layer.selected and layer != ignore:\n                layer.selected = False\n\n    def remove_selected(self):\n        \"\"\"Removes selected items from list.\n        \"\"\"\n        to_delete = []\n        for i in range(len(self)):\n            if self[i].selected:\n                to_delete.append(i)\n        to_delete.reverse()\n        for i in to_delete:\n            self.pop(i)\n", "path": "napari/components/layerlist.py"}], "after_files": [{"content": "from ..layers import Layer\n\nfrom ..util.naming import inc_name_count\nfrom ..util.list import ListModel\nfrom vispy.visuals.transforms import ChainTransform\n\n\ndef _add(event):\n    \"\"\"When a layer is added, set its name and order.\"\"\"\n    layers = event.source\n    layer = event.item\n    layer.name = layers._coerce_name(layer.name, layer)\n    layer._order = -len(layers)\n    layer.events.name.connect(lambda e: layers._update_name(e))\n    layers.unselect_all(ignore=layer)\n\n\ndef _remove(event):\n    \"\"\"When a layer is removed, remove its viewer.\"\"\"\n    layers = event.source\n    layer = event.item\n    layer._order = 0\n    layer._node.transforms = ChainTransform()\n    layer._node.parent = None\n\n\ndef _reorder(event):\n    \"\"\"When the list is reordered, propagate those changes to draw order.\"\"\"\n    layers = event.source\n    for i in range(len(layers)):\n        layers[i]._order = -i\n\n\nclass LayerList(ListModel):\n    \"\"\"List-like layer collection with built-in reordering and callback hooks.\n\n    Attributes\n    ----------\n    events : vispy.util.event.EmitterGroup\n        Event hooks:\n            * added(item, index): whenever an item is added\n            * removed(item): whenever an item is removed\n            * reordered(): whenever the list is reordered\n    \"\"\"\n\n    def __init__(self):\n        super().__init__(\n            basetype=Layer, lookup={str: lambda q, e: q == e.name}\n        )\n\n        self.events.added.connect(_add)\n        self.events.removed.connect(_remove)\n        self.events.reordered.connect(_reorder)\n\n    def __newlike__(self, iterable):\n        return ListModel(self._basetype, iterable, self._lookup)\n\n    def _coerce_name(self, name, layer=None):\n        \"\"\"Coerce a name into a unique equivalent.\n\n        Parameters\n        ----------\n        name : str\n            Original name.\n        layer : Layer, optional\n            Layer for which name is generated.\n\n        Returns\n        -------\n        new_name : str\n            Coerced, unique name.\n        \"\"\"\n        for l in self:\n            if l is layer:\n                continue\n            if l.name == name:\n                name = inc_name_count(name)\n\n        return name\n\n    def _update_name(self, event):\n        \"\"\"Coerce name of the layer in `event.layer`.\"\"\"\n        layer = event.source\n        layer.name = self._coerce_name(layer.name, layer)\n\n    def move_selected(self, index, insert):\n        \"\"\"Reorder list by moving the item at index and inserting it\n        at the insert index. If additional items are selected these will\n        get inserted at the insert index too. This allows for rearranging\n        the list based on dragging and dropping a selection of items, where\n        index is the index of the primary item being dragged, and insert is\n        the index of the drop location, and the selection indicates if\n        multiple items are being dragged. If the moved layer is not selected\n        select it.\n\n        Parameters\n        ----------\n        index : int\n            Index of primary item to be moved\n        insert : int\n            Index that item(s) will be inserted at\n        \"\"\"\n        total = len(self)\n        indices = list(range(total))\n        if not self[index].selected:\n            self.unselect_all()\n            self[index].selected = True\n        selected = [i for i in range(total) if self[i].selected]\n\n        # remove all indices to be moved\n        for i in selected:\n            indices.remove(i)\n        # adjust offset based on selected indices to move\n        offset = sum([i < insert and i != index for i in selected])\n        # insert indices to be moved at correct start\n        for insert_idx, elem_idx in enumerate(selected, start=insert - offset):\n            indices.insert(insert_idx, elem_idx)\n        # reorder list\n        self[:] = self[tuple(indices)]\n\n    def unselect_all(self, ignore=None):\n        \"\"\"Unselects all layers expect any specified in ignore.\n\n        Parameters\n        ----------\n        ignore : Layer | None\n            Layer that should not be unselected if specified.\n        \"\"\"\n        for layer in self:\n            if layer.selected and layer != ignore:\n                layer.selected = False\n\n    def remove_selected(self):\n        \"\"\"Removes selected items from list.\n        \"\"\"\n        to_delete = []\n        for i in range(len(self)):\n            if self[i].selected:\n                to_delete.append(i)\n        to_delete.reverse()\n        for i in to_delete:\n            self.pop(i)\n", "path": "napari/components/layerlist.py"}]}