Skip to content

Commit 21d3c86

Browse files
committed
Working slicer
1 parent c28254f commit 21d3c86

File tree

3 files changed

+112
-35
lines changed

3 files changed

+112
-35
lines changed

src/napari_matplotlib/base.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import napari
2-
from matplotlib.backends.backend_qt5agg import FigureCanvas, NavigationToolbar2QT
2+
from matplotlib.backends.backend_qt5agg import (
3+
FigureCanvas,
4+
NavigationToolbar2QT,
5+
)
36
from matplotlib.figure import Figure
47
from qtpy.QtWidgets import QVBoxLayout, QWidget
58

@@ -45,3 +48,36 @@ def current_z(self) -> int:
4548
Current z-step of the viewer.
4649
"""
4750
return self.viewer.dims.current_step[0]
51+
52+
53+
class SingleLayerWidget(NapariMPLWidget):
54+
"""
55+
Widget that takes a single napari layer as input.
56+
57+
This handles:
58+
59+
- updating the ``layer`` attribute when the selected layer is changed in
60+
the napari viewer
61+
- calling the ``draw()`` method when the current step is changed in the
62+
napari viewer
63+
64+
Attributes
65+
----------
66+
layer : `napari.layers.Layer`
67+
Currently selected layer.
68+
"""
69+
70+
def __init__(self, napari_viewer: napari.viewer.Viewer):
71+
super().__init__(napari_viewer)
72+
self.layer = self.viewer.layers[-1]
73+
self.viewer.layers.selection.events.active.connect(self.update_layer)
74+
self.viewer.dims.events.current_step.connect(self.draw)
75+
76+
def update_layer(self, event: napari.utils.events.Event) -> None:
77+
"""
78+
Update the currently selected layer.
79+
"""
80+
# Update current layer when selection changed in viewer
81+
if event.value:
82+
self.layer = event.value
83+
self.draw()

src/napari_matplotlib/histogram.py

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import napari
22
import numpy as np
33

4-
from .base import NapariMPLWidget
4+
from .base import SingleLayerWidget
55

66
__all__ = ["HistogramWidget"]
77

88

99
_COLORS = {"r": "tab:red", "g": "tab:green", "b": "tab:blue"}
1010

1111

12-
class HistogramWidget(NapariMPLWidget):
12+
class HistogramWidget(SingleLayerWidget):
1313
"""
1414
Widget to display a histogram of the currently selected layer.
1515
@@ -21,23 +21,9 @@ class HistogramWidget(NapariMPLWidget):
2121

2222
def __init__(self, napari_viewer: napari.viewer.Viewer):
2323
super().__init__(napari_viewer)
24-
self.layer = self.viewer.layers[-1]
24+
self.draw()
2525

26-
self.viewer.dims.events.current_step.connect(self.hist_current_layer)
27-
self.viewer.layers.selection.events.active.connect(self.update_layer)
28-
29-
self.hist_current_layer()
30-
31-
def update_layer(self, event: napari.utils.events.Event) -> None:
32-
"""
33-
Update the currently selected layer.
34-
"""
35-
# Update current layer when selection changed in viewer
36-
if event.value:
37-
self.layer = event.value
38-
self.hist_current_layer()
39-
40-
def hist_current_layer(self) -> None:
26+
def draw(self) -> None:
4127
"""
4228
Clear the axes and histogram the currently selected layer/slice.
4329
"""

src/napari_matplotlib/slice.py

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,94 @@
1+
from typing import Dict
2+
13
import napari
4+
import numpy as np
25
from qtpy.QtWidgets import QComboBox, QHBoxLayout, QSpinBox
36

4-
from napari_matplotlib.base import NapariMPLWidget
7+
from napari_matplotlib.base import SingleLayerWidget
58

69
__all__ = ["SliceWidget"]
710

811
_dims = ["x", "y", "z"]
912

1013

11-
class SliceWidget(NapariMPLWidget):
14+
class SliceWidget(SingleLayerWidget):
15+
"""
16+
Plot a 1D slice along a given dimension.
17+
"""
18+
1219
def __init__(self, napari_viewer: napari.viewer.Viewer):
1320
super().__init__(napari_viewer)
1421

15-
self.layer = self.viewer.layers[-1]
16-
1722
button_layout = QHBoxLayout()
1823
self.layout().addLayout(button_layout)
1924

2025
self.dim_selector = QComboBox()
2126
button_layout.addWidget(self.dim_selector)
27+
self.dim_selector.addItems(_dims)
2228

23-
self.selectors = {}
29+
self.slice_selectors = {}
2430
for d in _dims:
25-
self.selectors[d] = QSpinBox()
26-
button_layout.addWidget(self.selectors[d])
31+
self.slice_selectors[d] = QSpinBox()
32+
button_layout.addWidget(self.slice_selectors[d])
33+
34+
self.update_slice_selectors()
35+
self.viewer.dims.events.current_step.connect(self.draw)
36+
37+
self.draw()
38+
39+
@property
40+
def current_dim(self) -> str:
41+
"""
42+
Currently selected slice dimension.
43+
"""
44+
return self.dim_selector.currentText()
45+
46+
@property
47+
def current_dim_index(self) -> int:
48+
"""
49+
Currently selected slice dimension index.
50+
"""
51+
# Note the reversed list because in napari the z-axis is the first
52+
# numpy axis
53+
return _dims[::-1].index(self.current_dim)
2754

28-
self.update_dim_selector()
29-
self.viewer.layers.selection.events.changed.connect(
30-
self.update_dim_selector
31-
)
55+
@property
56+
def selector_values(self) -> Dict[str, int]:
57+
return {d: self.slice_selectors[d].value() for d in _dims}
3258

33-
def update_dim_selector(self) -> None:
59+
def update_slice_selectors(self) -> None:
3460
"""
35-
Update options in the dimension selector from currently selected layer.
61+
Update range and enabled status of the slice selectors, and the value
62+
of the z slice selector.
3663
"""
37-
dims = ["x", "y", "z"]
38-
self.dim_selector.clear()
39-
self.dim_selector.addItems(dims[0 : self.layer.data.ndim])
64+
# Update min/max
65+
for i, dim in enumerate(_dims):
66+
self.slice_selectors[dim].setRange(0, self.layer.data.shape[i])
67+
68+
# The z dimension is always set by current z in the viewer
69+
self.slice_selectors["z"].setValue(self.current_z)
70+
self.slice_selectors[self.current_dim].setEnabled(False)
71+
72+
def draw(self) -> None:
73+
self.update_slice_selectors()
74+
self.axes.clear()
75+
x = np.arange(self.layer.data.shape[self.current_dim_index])
76+
77+
slices = []
78+
for d in _dims:
79+
if d == self.current_dim:
80+
# Select all data along this axis
81+
slices.append(slice(None))
82+
else:
83+
# Select specific index
84+
val = self.selector_values[d]
85+
slices.append(slice(val, val + 1))
86+
87+
slices = slices[::-1]
88+
y = self.layer.data[tuple(slices)].ravel()
89+
90+
self.axes.plot(x, y)
91+
self.axes.set_xlabel(self.current_dim)
92+
self.axes.set_title(self.layer.name)
93+
94+
self.canvas.draw()

0 commit comments

Comments
 (0)