Repository: napari/napari

Issue:
AttributeError: 'LayerList' object has no attribute 'name'
## 🐛 Bug

- open napari, 
- Click on empty canvas.
- Press Ctrl-C
Console will show:

```
File ~/dev/napari/napari/_qt/containers/qt_layer_model.py:17, in QtLayerListModel.data(self=<napari._qt.containers.qt_layer_model.QtLayerListModel object>, index=<PyQt5.QtCore.QModelIndex object>, role=0)
     16 if role == Qt.DisplayRole:  # used for item text
---> 17     return layer.name
        layer = []
     18 if role == Qt.TextAlignmentRole:  # alignment of the text
     19     return Qt.AlignCenter

AttributeError: 'LayerList' object has no attribute 'name'
```

## Expected behavior

Don't.

## Environment

This seem to be because `_base_item_model:_BaseEventedItemModel.getItem` is not type stable.

```
    def getItem(self, index: QModelIndex) -> ItemType:
        """Return python object for a given `QModelIndex`.

        An invalid `QModelIndex` will return the root object.
        """
        return self._root[index.row()] if index.isValid() else self._root
```

The `else_self.root` is not of Type `ItemType`, but an (Empty) container.
Add PRs made after 0.4.15rc1 to release notes
... Like it says on the box. After this merges I'll make rc2.



Discussion:
I tried to understand the logic behind `_BaseEventedItemModel`, but I think that may needs @tlambert03 
I created this PR to fix the issues found when testing the new constructor installs, hopefully the RC will include https://github.com/napari/napari/pull/4214

--- 

I also added some extra tests that are effectively failing now on https://github.com/napari/napari/pull/4216, but they will pass with https://github.com/napari/napari/pull/4214
# [Codecov](https://codecov.io/gh/napari/napari/pull/4217?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#4217](https://codecov.io/gh/napari/napari/pull/4217?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (4c056c8) into [main](https://codecov.io/gh/napari/napari/commit/2455d07bf3d7acf6873806d176608d82037cdc5a?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (2455d07) will **decrease** coverage by `0.02%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/napari/napari/pull/4217/graphs/tree.svg?width=650&height=150&src=pr&token=1LfdDeezHt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)](https://codecov.io/gh/napari/napari/pull/4217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)

```diff
@@            Coverage Diff             @@
##             main    #4217      +/-   ##
==========================================
- Coverage   83.39%   83.37%   -0.03%     
==========================================
  Files         593      593              
  Lines       49464    49483      +19     
==========================================
+ Hits        41252    41255       +3     
- Misses       8212     8228      +16     
```


| [Impacted Files](https://codecov.io/gh/napari/napari/pull/4217?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [napari/utils/misc.py](https://codecov.io/gh/napari/napari/pull/4217/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bmFwYXJpL3V0aWxzL21pc2MucHk=) | `77.35% <0.00%> (-3.29%)` | :arrow_down: |
| [napari/\_qt/dialogs/qt\_plugin\_dialog.py](https://codecov.io/gh/napari/napari/pull/4217/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bmFwYXJpL19xdC9kaWFsb2dzL3F0X3BsdWdpbl9kaWFsb2cucHk=) | `12.56% <0.00%> (-0.16%)` | :arrow_down: |
| [napari/utils/action\_manager.py](https://codecov.io/gh/napari/napari/pull/4217/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bmFwYXJpL3V0aWxzL2FjdGlvbl9tYW5hZ2VyLnB5) | `84.46% <0.00%> (+0.97%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/napari/napari/pull/4217?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/napari/napari/pull/4217?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Last update [2455d07...4c056c8](https://codecov.io/gh/napari/napari/pull/4217?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).


Resolving patch:
diff --git a/napari/_qt/containers/_layer_delegate.py b/napari/_qt/containers/_layer_delegate.py
--- a/napari/_qt/containers/_layer_delegate.py
+++ b/napari/_qt/containers/_layer_delegate.py
@@ -91,6 +91,8 @@ def paint(
     def get_layer_icon(self, option, index):
         """Add the appropriate QIcon to the item based on the layer type."""
         layer = index.data(ItemRole)
+        if layer is None:
+            return
         if hasattr(layer, 'is_group') and layer.is_group():  # for layer trees
             expanded = option.widget.isExpanded(index)
             icon_name = 'folder-open' if expanded else 'folder'
diff --git a/napari/_qt/containers/qt_layer_model.py b/napari/_qt/containers/qt_layer_model.py
--- a/napari/_qt/containers/qt_layer_model.py
+++ b/napari/_qt/containers/qt_layer_model.py
@@ -10,6 +10,8 @@
 class QtLayerListModel(QtListModel[Layer]):
     def data(self, index: QModelIndex, role: Qt.ItemDataRole):
         """Return data stored under ``role`` for the item at ``index``."""
+        if not index.isValid():
+            return None
         layer = self.getItem(index)
         if role == Qt.DisplayRole:  # used for item text
             return layer.name


Test patch: