Repository: napari/napari

Issue:
Label properties status message doesn't work for multiscale labels
## 🐛 Bug

In #1281, we added the ability of labels layers to receive a properties dictionary, which would then be displayed per-label in the status bar. This is working great but is broken when using a multiscale labels pyramid.

## To Reproduce

Steps to reproduce the behavior:

First, try it out with single-scale labels:

```python
import numpy as np
import napari

labels = np.zeros((10_000, 10_000))
labels[5000:] += 1
labels[:, 5000:] += 2

properties = {'class': ['background', 'pears', 'apples', 'oranges']}

v = napari.view_labels(labels, properties=properties)
```

This gives the following behaviour:

<img width="1200" alt="Screen Shot 2020-07-02 at 3 11 53 pm" src="https://user-images.githubusercontent.com/492549/86319453-ed80e900-bc77-11ea-853c-e57e2b1f7aa8.png">

Now, try:

```python
labels_pyramid = [labels[::i, ::i] for i in [1, 2, 4, 8]]

v = napari.view_labels(labels_pyramid, properties=properties)
```

Now, it appears that the properties for each label does not exist:

<img width="1200" alt="Screen Shot 2020-07-02 at 3 15 51 pm" src="https://user-images.githubusercontent.com/492549/86319468-f70a5100-bc77-11ea-8bcd-299353f82577.png">

This happens regardless of the zoom level.

## Expected behavior

The property corresponding to the label value under the cursor should be displayed, regardless of zoom level.

## Environment

```
napari: 0.3.6.dev11+ge43f730
Platform: Darwin-18.7.0-x86_64-i386-64bit
Python: 3.7.4 (default, Aug 13 2019, 15:17:50)  [Clang 4.0.1 (tags/RELEASE_401/final)]
Qt: 5.14.2
PyQt5: 5.14.2
NumPy: 1.17.2
SciPy: 1.3.1
Dask: 2.6.0
VisPy: 0.6.4

GL version:  2.1 NVIDIA-12.0.24 355.11.10.50.10.103
MAX_TEXTURE_SIZE: 16384

Plugins:
  - napari-plugin-engine: 0.1.6
  - svg: 0.1.3
```


Discussion:

Resolving patch:
diff --git a/napari/layers/labels/labels.py b/napari/layers/labels/labels.py
--- a/napari/layers/labels/labels.py
+++ b/napari/layers/labels/labels.py
@@ -742,8 +742,12 @@ def get_message(self):
         if self._label_index and self._properties:
             # if the cursor is not outside the image or on the background
             if self._value is not None:
-                if self._value in self._label_index:
-                    idx = self._label_index[self._value]
+                if self.multiscale:
+                    value = self._value[1]
+                else:
+                    value = self._value
+                if value in self._label_index:
+                    idx = self._label_index[value]
                     for k, v in self._properties.items():
                         if k != 'index':
                             msg += f', {k}: {v[idx]}'


Test patch:
diff --git a/napari/layers/labels/_tests/test_labels.py b/napari/layers/labels/_tests/test_labels.py
--- a/napari/layers/labels/_tests/test_labels.py
+++ b/napari/layers/labels/_tests/test_labels.py
@@ -236,6 +236,41 @@ def test_properties():
     assert layer_message.endswith('Class 12')
 
 
+def test_multiscale_properties():
+    """Test adding labels with multiscale properties."""
+    np.random.seed(0)
+    data0 = np.random.randint(20, size=(10, 15))
+    data1 = data0[::2, ::2]
+    data = [data0, data1]
+
+    layer = Labels(data)
+    assert isinstance(layer.properties, dict)
+    assert len(layer.properties) == 0
+
+    properties = {'class': ['Background'] + [f'Class {i}' for i in range(20)]}
+    label_index = {i: i for i in range(len(properties['class']))}
+    layer = Labels(data, properties=properties)
+    assert isinstance(layer.properties, dict)
+    assert layer.properties == properties
+    assert layer._label_index == label_index
+
+    current_label = layer.get_value()[1]
+    layer_message = layer.get_message()
+    assert layer_message.endswith(f'Class {current_label - 1}')
+
+    properties = {'class': ['Background']}
+    layer = Labels(data, properties=properties)
+    layer_message = layer.get_message()
+    assert layer_message.endswith("[No Properties]")
+
+    properties = {'class': ['Background', 'Class 12'], 'index': [0, 12]}
+    label_index = {0: 0, 12: 1}
+    layer = Labels(data, properties=properties)
+    layer_message = layer.get_message()
+    assert layer._label_index == label_index
+    assert layer_message.endswith('Class 12')
+
+
 def test_colormap():
     """Test colormap."""
     np.random.seed(0)