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,46 @@ 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 not len (self .layers ):
62
+ return
63
+ if self .current_dim_name == "x" :
64
+ max = self ._layer .data .shape [- 2 ]
65
+ elif self .current_dim_name == "y" :
66
+ max = self ._layer .data .shape [- 1 ]
67
+ else :
68
+ raise RuntimeError ("dim name must be x or y" )
69
+ self .slice_selector .setRange (0 , max - 1 )
70
+
71
+ @property
72
+ def _slice_width (self ) -> int :
73
+ """
74
+ Width of the slice being plotted.
75
+ """
76
+ return self ._layer .data .shape [self .current_dim_index ]
77
+
55
78
@property
56
79
def _layer (self ) -> napari .layers .Layer :
57
80
"""
@@ -73,7 +96,7 @@ def current_dim_index(self) -> int:
73
96
"""
74
97
# Note the reversed list because in napari the z-axis is the first
75
98
# numpy axis
76
- return self ._dim_names [:: - 1 ] .index (self .current_dim_name )
99
+ return self ._dim_names .index (self .current_dim_name )
77
100
78
101
@property
79
102
def _dim_names (self ) -> List [str ]:
@@ -82,45 +105,31 @@ def _dim_names(self) -> List[str]:
82
105
dimensionality of the currently selected data.
83
106
"""
84
107
if self ._layer .data .ndim == 2 :
85
- return ["x " , "y " ]
108
+ return ["y " , "x " ]
86
109
elif self ._layer .data .ndim == 3 :
87
- return ["x " , "y" , "z " ]
110
+ return ["z " , "y" , "x " ]
88
111
else :
89
112
raise RuntimeError ("Don't know how to handle ndim != 2 or 3" )
90
113
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
114
def _get_xy (self ) -> Tuple [npt .NDArray [Any ], npt .NDArray [Any ]]:
101
115
"""
102
116
Get data for plotting.
103
117
"""
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 })
118
+ val = self .slice_selector .value ()
111
119
112
120
slices = []
113
121
for dim_name in self ._dim_names :
114
122
if dim_name == self .current_dim_name :
115
123
# Select all data along this axis
116
124
slices .append (slice (None ))
125
+ elif dim_name == "z" :
126
+ # Only select the currently viewed z-index
127
+ slices .append (slice (self .current_z , self .current_z + 1 ))
117
128
else :
118
129
# Select specific index
119
- val = vals [dim_name ]
120
130
slices .append (slice (val , val + 1 ))
121
131
122
- # Reverse since z is the first axis in napari
123
- slices = slices [::- 1 ]
132
+ x = np .arange (self ._slice_width )
124
133
y = self ._layer .data [tuple (slices )].ravel ()
125
134
126
135
return x , y
0 commit comments