Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f2a28d6

Browse files
committed
fix merge conflict
2 parents 9f4cbb9 + 1ec14d3 commit f2a28d6

File tree

11 files changed

+162
-6
lines changed

11 files changed

+162
-6
lines changed

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,19 @@ A plugin to create Matplotlib plots from napari layers
1414
## Introduction
1515
`napari-matplotlib` is a bridge between `napari` and `matplotlib`, making it easy to create publication quality `Matplotlib` plots based on the data loaded in `napari` layers.
1616

17-
Currently you can:
17+
## Available widgets
1818

19-
- Draw histograms of individual image layers
20-
- Scatter plot two image layers against each other
19+
### `Slice`
20+
Plots 1D slices of data along a specified axis.
21+
![](https://raw.githubusercontent.com/dstansby/napari-matplotlib/main/examples/slice.png)
2122

22-
Here's a demo of the scatter widget:
23+
### `Histogram`
24+
Plots histograms of individual image layers, or RGB histograms of an RGB image
25+
![](https://raw.githubusercontent.com/dstansby/napari-matplotlib/main/examples/hist.png)
2326

24-
![](https://raw.githubusercontent.com/dstansby/napari-matplotlib/main/examples/short_scatter.gif)
27+
### `Scatter`
28+
Scatters the values of two similarly sized images layers against each other.
29+
![](https://raw.githubusercontent.com/dstansby/napari-matplotlib/main/examples/scatter.png)
2530

2631
## Installation
2732

examples/hist.png

1.06 MB
Loading

examples/scatter.png

1.09 MB
Loading

examples/short_scatter.gif

-3.12 MB
Binary file not shown.

examples/slice.png

1.11 MB
Loading

examples/slice.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""
2+
1D slices
3+
=========
4+
"""
5+
import napari
6+
7+
viewer = napari.Viewer()
8+
viewer.open_sample("napari", "kidney")
9+
10+
viewer.window.add_plugin_dock_widget(
11+
plugin_name="napari-matplotlib", widget_name="1D slice"
12+
)
13+
14+
if __name__ == "__main__":
15+
napari.run()

src/napari_matplotlib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66

77
from .histogram import * # NoQA
88
from .scatter import * # NoQA
9+
from .slice import * # NoQA

src/napari_matplotlib/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def setup_callbacks(self) -> None:
8383

8484
def update_layers(self, event: napari.utils.events.Event) -> None:
8585
"""
86-
Update the currently selected layers and re-draw.
86+
Update the layers attribute with currently selected layers and re-draw.
8787
"""
8888
self.layers = list(self.viewer.layers.selection)
8989
self._on_update_layers()

src/napari_matplotlib/napari.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ contributions:
1414
python_name: napari_matplotlib:FeaturesScatterWidget
1515
title: Make a scatter plot of layer features
1616

17+
- id: napari-matplotlib.slice
18+
python_name: napari_matplotlib:SliceWidget
19+
title: Plot a 1D slice
20+
1721
widgets:
1822
- command: napari-matplotlib.histogram
1923
display_name: Histogram
@@ -23,3 +27,6 @@ contributions:
2327

2428
- command: napari-matplotlib.features_scatter
2529
display_name: FeaturesScatter
30+
31+
- command: napari-matplotlib.slice
32+
display_name: 1D slice

src/napari_matplotlib/slice.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
from typing import Dict, Tuple
2+
3+
import napari
4+
import numpy as np
5+
from qtpy.QtWidgets import QComboBox, QHBoxLayout, QLabel, QSpinBox
6+
7+
from napari_matplotlib.base import NapariMPLWidget
8+
9+
__all__ = ["SliceWidget"]
10+
11+
_dims_sel = ["x", "y"]
12+
_dims = ["x", "y", "z"]
13+
14+
15+
class SliceWidget(NapariMPLWidget):
16+
"""
17+
Plot a 1D slice along a given dimension.
18+
"""
19+
20+
n_layers_input = 1
21+
22+
def __init__(self, napari_viewer: napari.viewer.Viewer):
23+
# Setup figure/axes
24+
super().__init__(napari_viewer)
25+
self.axes = self.canvas.figure.subplots()
26+
27+
button_layout = QHBoxLayout()
28+
self.layout().addLayout(button_layout)
29+
30+
self.dim_selector = QComboBox()
31+
button_layout.addWidget(QLabel("Slice axis:"))
32+
button_layout.addWidget(self.dim_selector)
33+
self.dim_selector.addItems(_dims)
34+
35+
self.slice_selectors = {}
36+
for d in _dims_sel:
37+
self.slice_selectors[d] = QSpinBox()
38+
button_layout.addWidget(QLabel(f"{d}:"))
39+
button_layout.addWidget(self.slice_selectors[d])
40+
41+
# Setup callbacks
42+
# Re-draw when any of the combon/spin boxes are updated
43+
self.dim_selector.currentTextChanged.connect(self._draw)
44+
for d in _dims_sel:
45+
self.slice_selectors[d].textChanged.connect(self._draw)
46+
47+
self.update_layers(None)
48+
49+
@property
50+
def layer(self):
51+
return self.layers[0]
52+
53+
@property
54+
def current_dim(self) -> str:
55+
"""
56+
Currently selected slice dimension.
57+
"""
58+
return self.dim_selector.currentText()
59+
60+
@property
61+
def current_dim_index(self) -> int:
62+
"""
63+
Currently selected slice dimension index.
64+
"""
65+
# Note the reversed list because in napari the z-axis is the first
66+
# numpy axis
67+
return _dims[::-1].index(self.current_dim)
68+
69+
@property
70+
def selector_values(self) -> Dict[str, int]:
71+
return {d: self.slice_selectors[d].value() for d in _dims_sel}
72+
73+
def update_slice_selectors(self) -> None:
74+
"""
75+
Update range and enabled status of the slice selectors, and the value
76+
of the z slice selector.
77+
"""
78+
# Update min/max
79+
for i, dim in enumerate(_dims_sel):
80+
self.slice_selectors[dim].setRange(0, self.layer.data.shape[i])
81+
82+
def get_xy(self) -> Tuple[np.ndarray, np.ndarray]:
83+
"""
84+
Get data for plotting.
85+
"""
86+
x = np.arange(self.layer.data.shape[self.current_dim_index])
87+
88+
vals = self.selector_values
89+
vals.update({"z": self.current_z})
90+
91+
slices = []
92+
for d in _dims:
93+
if d == self.current_dim:
94+
# Select all data along this axis
95+
slices.append(slice(None))
96+
else:
97+
# Select specific index
98+
val = vals[d]
99+
slices.append(slice(val, val + 1))
100+
101+
# Reverse since z is the first axis in napari
102+
slices = slices[::-1]
103+
y = self.layer.data[tuple(slices)].ravel()
104+
105+
return x, y
106+
107+
def clear(self) -> None:
108+
self.axes.cla()
109+
110+
def draw(self) -> None:
111+
"""
112+
Clear axes and draw a 1D plot.
113+
"""
114+
x, y = self.get_xy()
115+
116+
self.axes.plot(x, y)
117+
self.axes.set_xlabel(self.current_dim)
118+
self.axes.set_title(self.layer.name)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import numpy as np
2+
3+
from napari_matplotlib import SliceWidget
4+
5+
6+
def test_scatter(make_napari_viewer):
7+
# Smoke test adding a histogram widget
8+
viewer = make_napari_viewer()
9+
viewer.add_image(np.random.random((100, 100, 100)))
10+
SliceWidget(viewer)

0 commit comments

Comments
 (0)