7
7
from napari .layers import Image
8
8
from napari .layers ._multiscale_data import MultiScaleData
9
9
from qtpy .QtWidgets import (
10
- QAbstractSpinBox ,
11
10
QComboBox ,
12
- QDoubleSpinBox ,
13
11
QFormLayout ,
14
12
QGroupBox ,
15
13
QLabel ,
30
28
def _get_bins (
31
29
data : npt .NDArray [Any ],
32
30
num_bins : int = 100 ,
33
- start : int | float | None = None ,
34
- stop : int | float | None = None ,
35
31
) -> npt .NDArray [Any ]:
36
32
"""Create evenly spaced bins with a given interval.
37
33
38
- If `start` or `stop` are `None`, they will be set based on the minimum
39
- and maximum values, respectively, of the data.
40
-
41
34
Parameters
42
35
----------
43
36
data : napari.layers.Layer.data
44
37
Napari layer data.
45
38
num_bins : integer, optional
46
- Number of evenly-spaced bins to create.
47
- start : integer or real, optional
48
- Start bin edge. Defaults to the minimum value of `data`.
49
- stop : integer or real, optional
50
- Stop bin edge. Defaults to the maximum value of `data`.
39
+ Number of evenly-spaced bins to create. Defaults to 100.
51
40
52
41
Returns
53
42
-------
54
43
bin_edges : numpy.ndarray
55
44
Array of evenly spaced bin edges.
56
45
"""
57
- start = np .min (data ) if start is None else start
58
- stop = np .max (data ) if stop is None else stop
59
-
60
46
if data .dtype .kind in {"i" , "u" }:
61
47
# Make sure integer data types have integer sized bins
62
- step = np .ceil (( stop - start ) / num_bins )
63
- return np .arange (start , stop + step , step )
48
+ step = np .ceil (np . ptp ( data ) / num_bins )
49
+ return np .arange (np . min ( data ), np . max ( data ) + step , step )
64
50
else :
65
51
# For other data types we can use exactly `num_bins` bins
66
52
# (and `num_bins` + 1 bin edges)
67
- return np .linspace (start , stop , num_bins + 1 )
53
+ return np .linspace (np . min ( data ), np . max ( data ) , num_bins + 1 )
68
54
69
55
70
56
class HistogramWidget (SingleAxesWidget ):
@@ -82,53 +68,28 @@ def __init__(
82
68
):
83
69
super ().__init__ (napari_viewer , parent = parent )
84
70
85
- # Create widgets for setting bin parameters
86
- bins_start = QDoubleSpinBox ()
87
- bins_start .setStepType (QAbstractSpinBox .AdaptiveDecimalStepType )
88
- bins_start .setRange (- 1e10 , 1e10 )
89
- bins_start .setValue (0 )
90
- bins_start .setWrapping (False )
91
- bins_start .setKeyboardTracking (False )
92
- bins_start .setDecimals (2 )
93
-
94
- bins_stop = QDoubleSpinBox ()
95
- bins_stop .setStepType (QAbstractSpinBox .AdaptiveDecimalStepType )
96
- bins_stop .setRange (- 1e10 , 1e10 )
97
- bins_stop .setValue (100 )
98
- bins_start .setWrapping (False )
99
- bins_stop .setKeyboardTracking (False )
100
- bins_stop .setDecimals (2 )
101
-
102
- bins_num = QSpinBox ()
103
- bins_num .setRange (1 , 100_000 )
104
- bins_num .setValue (101 )
105
- bins_num .setWrapping (False )
106
- bins_num .setKeyboardTracking (False )
71
+ num_bins_widget = QSpinBox ()
72
+ num_bins_widget .setRange (1 , 100_000 )
73
+ num_bins_widget .setValue (101 )
74
+ num_bins_widget .setWrapping (False )
75
+ num_bins_widget .setKeyboardTracking (False )
107
76
108
77
# Set bins widget layout
109
78
bins_selection_layout = QFormLayout ()
110
- bins_selection_layout .addRow ("start" , bins_start )
111
- bins_selection_layout .addRow ("stop" , bins_stop )
112
- bins_selection_layout .addRow ("num" , bins_num )
79
+ bins_selection_layout .addRow ("num bins" , num_bins_widget )
113
80
114
81
# Group the widgets and add to main layout
115
- bins_widget_group = QGroupBox ("Bins " )
116
- bins_widget_group_layout = QVBoxLayout ()
117
- bins_widget_group_layout .addLayout (bins_selection_layout )
118
- bins_widget_group .setLayout (bins_widget_group_layout )
119
- self .layout ().addWidget (bins_widget_group )
82
+ params_widget_group = QGroupBox ("Params " )
83
+ params_widget_group_layout = QVBoxLayout ()
84
+ params_widget_group_layout .addLayout (bins_selection_layout )
85
+ params_widget_group .setLayout (params_widget_group_layout )
86
+ self .layout ().addWidget (params_widget_group )
120
87
121
88
# Add callbacks
122
- bins_start .valueChanged .connect (self ._draw )
123
- bins_stop .valueChanged .connect (self ._draw )
124
- bins_num .valueChanged .connect (self ._draw )
89
+ num_bins_widget .valueChanged .connect (self ._draw )
125
90
126
91
# Store widgets for later usage
127
- self ._bin_widgets = {
128
- "start" : bins_start ,
129
- "stop" : bins_stop ,
130
- "num" : bins_num ,
131
- }
92
+ self .num_bins_widget = num_bins_widget
132
93
133
94
self ._update_layers (None )
134
95
self .viewer .events .theme .connect (self ._on_napari_theme_changed )
@@ -144,27 +105,9 @@ def on_update_layers(self) -> None:
144
105
if not self .layers :
145
106
return
146
107
147
- # Reset the bin start, stop and step values based on new layer data
108
+ # Reset the num bins based on new layer data
148
109
layer_data = self ._get_layer_data (self .layers [0 ])
149
- self .autoset_widget_bins (data = layer_data )
150
-
151
- # Only allow integer bins for integer data
152
- # And only allow values greater than 0 for unsigned integers
153
- n_decimals = 0 if np .issubdtype (layer_data .dtype , np .integer ) else 2
154
- is_unsigned = layer_data .dtype .kind == "u"
155
- minimum_value = 0 if is_unsigned else - 1e10
156
-
157
- # Disable callbacks whilst widget values might change
158
- for widget in self ._bin_widgets .values ():
159
- widget .blockSignals (True )
160
-
161
- self ._bin_widgets ["start" ].setDecimals (n_decimals )
162
- self ._bin_widgets ["stop" ].setDecimals (n_decimals )
163
- self ._bin_widgets ["start" ].setMinimum (minimum_value )
164
- self ._bin_widgets ["stop" ].setMinimum (minimum_value )
165
-
166
- for widget in self ._bin_widgets .values ():
167
- widget .blockSignals (False )
110
+ self ._set_widget_nums_bins (data = layer_data )
168
111
169
112
def _update_contrast_lims (self ) -> None :
170
113
for lim , line in zip (
@@ -174,50 +117,10 @@ def _update_contrast_lims(self) -> None:
174
117
175
118
self .figure .canvas .draw ()
176
119
177
- def autoset_widget_bins (self , data : npt .NDArray [Any ]) -> None :
178
- """Update widgets with bins determined from the image data"""
120
+ def _set_widget_nums_bins (self , data : npt .NDArray [Any ]) -> None :
121
+ """Update num_bins widget with bins determined from the image data"""
179
122
bins = _get_bins (data )
180
-
181
- # Disable callbacks whilst setting widget values
182
- for widget in self ._bin_widgets .values ():
183
- widget .blockSignals (True )
184
-
185
- self .bins_start = bins [0 ]
186
- self .bins_stop = bins [- 1 ]
187
- self .bins_num = bins .size - 1
188
-
189
- for widget in self ._bin_widgets .values ():
190
- widget .blockSignals (False )
191
-
192
- @property
193
- def bins_start (self ) -> float :
194
- """Minimum bin edge"""
195
- return self ._bin_widgets ["start" ].value ()
196
-
197
- @bins_start .setter
198
- def bins_start (self , start : int | float ) -> None :
199
- """Set the minimum bin edge"""
200
- self ._bin_widgets ["start" ].setValue (start )
201
-
202
- @property
203
- def bins_stop (self ) -> float :
204
- """Maximum bin edge"""
205
- return self ._bin_widgets ["stop" ].value ()
206
-
207
- @bins_stop .setter
208
- def bins_stop (self , stop : int | float ) -> None :
209
- """Set the maximum bin edge"""
210
- self ._bin_widgets ["stop" ].setValue (stop )
211
-
212
- @property
213
- def bins_num (self ) -> int :
214
- """Number of bins to use"""
215
- return self ._bin_widgets ["num" ].value ()
216
-
217
- @bins_num .setter
218
- def bins_num (self , num : int ) -> None :
219
- """Set the number of bins to use"""
220
- self ._bin_widgets ["num" ].setValue (num )
123
+ self .num_bins_widget .setValue (bins .size - 1 )
221
124
222
125
def _get_layer_data (self , layer : napari .layers .Layer ) -> npt .NDArray [Any ]:
223
126
"""Get the data associated with a given layer"""
@@ -248,9 +151,7 @@ def draw(self) -> None:
248
151
# whole cube into memory.
249
152
bins = _get_bins (
250
153
data ,
251
- num_bins = self .bins_num ,
252
- start = self .bins_start ,
253
- stop = self .bins_stop ,
154
+ num_bins = self .num_bins_widget .value (),
254
155
)
255
156
256
157
if layer .rgb :
0 commit comments