1
- from typing import Any , Dict , List , Optional , Tuple
1
+ from typing import Any , List , Optional , Tuple
2
2
3
3
import matplotlib .ticker as mticker
4
4
import napari
5
5
import numpy as np
6
6
import numpy .typing as npt
7
- from qtpy .QtWidgets import QComboBox , QHBoxLayout , QLabel , QSpinBox , QWidget
7
+ from qtpy .QtCore import Qt
8
+ from qtpy .QtWidgets import (
9
+ QComboBox ,
10
+ QLabel ,
11
+ QSlider ,
12
+ QVBoxLayout ,
13
+ QWidget ,
14
+ )
8
15
9
16
from .base import SingleAxesWidget
10
17
from .util import Interval
11
18
12
19
__all__ = ["SliceWidget" ]
13
20
14
- _dims_sel = ["x" , "y" ]
15
-
16
21
17
22
class SliceWidget (SingleAxesWidget ):
18
23
"""
@@ -30,28 +35,44 @@ def __init__(
30
35
# Setup figure/axes
31
36
super ().__init__ (napari_viewer , parent = parent )
32
37
33
- button_layout = QHBoxLayout ()
34
- self .layout ().addLayout (button_layout )
35
-
36
38
self .dim_selector = QComboBox ()
39
+ self .dim_selector .addItems (["x" , "y" ])
40
+
41
+ self .slice_selector = QSlider (orientation = Qt .Orientation .Horizontal )
42
+
43
+ # Create widget layout
44
+ button_layout = QVBoxLayout ()
37
45
button_layout .addWidget (QLabel ("Slice axis:" ))
38
46
button_layout .addWidget (self .dim_selector )
39
- self .dim_selector .addItems (["x" , "y" , "z" ])
40
-
41
- self .slice_selectors = {}
42
- for d in _dims_sel :
43
- self .slice_selectors [d ] = QSpinBox ()
44
- button_layout .addWidget (QLabel (f"{ d } :" ))
45
- button_layout .addWidget (self .slice_selectors [d ])
47
+ button_layout .addWidget (self .slice_selector )
48
+ self .layout ().addLayout (button_layout )
46
49
47
50
# Setup callbacks
48
- # Re-draw when any of the combon/spin boxes are updated
51
+ # Re-draw when any of the combo/slider is updated
49
52
self .dim_selector .currentTextChanged .connect (self ._draw )
50
- for d in _dims_sel :
51
- self .slice_selectors [d ].textChanged .connect (self ._draw )
53
+ self .slice_selector .valueChanged .connect (self ._draw )
52
54
53
55
self ._update_layers (None )
54
56
57
+ def on_update_layers (self ) -> None :
58
+ """
59
+ Called when layer selection is updated.
60
+ """
61
+ if self .current_dim_name == "x" :
62
+ max = self ._layer .data .shape [- 2 ]
63
+ elif self .current_dim_name == "y" :
64
+ max = self ._layer .data .shape [- 1 ]
65
+ else :
66
+ raise RuntimeError ("dim name must be x or y" )
67
+ self .slice_selector .setRange (0 , max )
68
+
69
+ @property
70
+ def _slice_width (self ) -> int :
71
+ """
72
+ Width of the slice being plotted.
73
+ """
74
+ return self ._layer .data .shape [self .current_dim_index ] - 1
75
+
55
76
@property
56
77
def _layer (self ) -> napari .layers .Layer :
57
78
"""
@@ -73,7 +94,7 @@ def current_dim_index(self) -> int:
73
94
"""
74
95
# Note the reversed list because in napari the z-axis is the first
75
96
# numpy axis
76
- return self ._dim_names [:: - 1 ] .index (self .current_dim_name )
97
+ return self ._dim_names .index (self .current_dim_name )
77
98
78
99
@property
79
100
def _dim_names (self ) -> List [str ]:
@@ -82,45 +103,31 @@ def _dim_names(self) -> List[str]:
82
103
dimensionality of the currently selected data.
83
104
"""
84
105
if self ._layer .data .ndim == 2 :
85
- return ["x " , "y " ]
106
+ return ["y " , "x " ]
86
107
elif self ._layer .data .ndim == 3 :
87
- return ["x " , "y" , "z " ]
108
+ return ["z " , "y" , "x " ]
88
109
else :
89
110
raise RuntimeError ("Don't know how to handle ndim != 2 or 3" )
90
111
91
- @property
92
- def _selector_values (self ) -> Dict [str , int ]:
93
- """
94
- Values of the slice selectors.
95
-
96
- Mapping from dimension name to value.
97
- """
98
- return {d : self .slice_selectors [d ].value () for d in _dims_sel }
99
-
100
112
def _get_xy (self ) -> Tuple [npt .NDArray [Any ], npt .NDArray [Any ]]:
101
113
"""
102
114
Get data for plotting.
103
115
"""
104
- dim_index = self .current_dim_index
105
- if self ._layer .data .ndim == 2 :
106
- dim_index -= 1
107
- x = np .arange (self ._layer .data .shape [dim_index ])
108
-
109
- vals = self ._selector_values
110
- vals .update ({"z" : self .current_z })
116
+ val = self .slice_selector .value ()
111
117
112
118
slices = []
113
119
for dim_name in self ._dim_names :
114
120
if dim_name == self .current_dim_name :
115
121
# Select all data along this axis
116
122
slices .append (slice (None ))
123
+ elif dim_name == "z" :
124
+ # Only select the currently viewed z-index
125
+ slices .append (slice (self .current_z , self .current_z + 1 ))
117
126
else :
118
127
# Select specific index
119
- val = vals [dim_name ]
120
128
slices .append (slice (val , val + 1 ))
121
129
122
- # Reverse since z is the first axis in napari
123
- slices = slices [::- 1 ]
130
+ x = np .arange (self ._slice_width )
124
131
y = self ._layer .data [tuple (slices )].ravel ()
125
132
126
133
return x , y
0 commit comments