Skip to content

Commit 67a8641

Browse files
committed
remove widgest to set start and stop values for histogram bins
1 parent 426a0f5 commit 67a8641

File tree

3 files changed

+24
-125
lines changed

3 files changed

+24
-125
lines changed

Diff for: src/napari_matplotlib/histogram.py

+23-122
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
from napari.layers import Image
88
from napari.layers._multiscale_data import MultiScaleData
99
from qtpy.QtWidgets import (
10-
QAbstractSpinBox,
1110
QComboBox,
12-
QDoubleSpinBox,
1311
QFormLayout,
1412
QGroupBox,
1513
QLabel,
@@ -30,41 +28,29 @@
3028
def _get_bins(
3129
data: npt.NDArray[Any],
3230
num_bins: int = 100,
33-
start: int | float | None = None,
34-
stop: int | float | None = None,
3531
) -> npt.NDArray[Any]:
3632
"""Create evenly spaced bins with a given interval.
3733
38-
If `start` or `stop` are `None`, they will be set based on the minimum
39-
and maximum values, respectively, of the data.
40-
4134
Parameters
4235
----------
4336
data : napari.layers.Layer.data
4437
Napari layer data.
4538
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.
5140
5241
Returns
5342
-------
5443
bin_edges : numpy.ndarray
5544
Array of evenly spaced bin edges.
5645
"""
57-
start = np.min(data) if start is None else start
58-
stop = np.max(data) if stop is None else stop
59-
6046
if data.dtype.kind in {"i", "u"}:
6147
# 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)
6450
else:
6551
# For other data types we can use exactly `num_bins` bins
6652
# (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)
6854

6955

7056
class HistogramWidget(SingleAxesWidget):
@@ -82,53 +68,28 @@ def __init__(
8268
):
8369
super().__init__(napari_viewer, parent=parent)
8470

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)
10776

10877
# Set bins widget layout
10978
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)
11380

11481
# 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)
12087

12188
# 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)
12590

12691
# 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
13293

13394
self._update_layers(None)
13495
self.viewer.events.theme.connect(self._on_napari_theme_changed)
@@ -144,27 +105,9 @@ def on_update_layers(self) -> None:
144105
if not self.layers:
145106
return
146107

147-
# Reset the bin start, stop and step values based on new layer data
108+
# Reset the num bins based on new layer data
148109
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)
168111

169112
def _update_contrast_lims(self) -> None:
170113
for lim, line in zip(
@@ -174,50 +117,10 @@ def _update_contrast_lims(self) -> None:
174117

175118
self.figure.canvas.draw()
176119

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"""
179122
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)
221124

222125
def _get_layer_data(self, layer: napari.layers.Layer) -> npt.NDArray[Any]:
223126
"""Get the data associated with a given layer"""
@@ -248,9 +151,7 @@ def draw(self) -> None:
248151
# whole cube into memory.
249152
bins = _get_bins(
250153
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(),
254155
)
255156

256157
if layer.rgb:
-62 Bytes
Loading

Diff for: src/napari_matplotlib/tests/test_histogram.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ def test_histogram_2D_bins(make_napari_viewer, astronaut_data):
1717
viewer.add_image(astronaut_data[0], **astronaut_data[1])
1818
widget = HistogramWidget(viewer)
1919
viewer.window.add_dock_widget(widget)
20-
widget.bins_start = 0
21-
widget.bins_stop = 350
22-
widget.bins_num = 35
20+
widget.num_bins_widget.setValue(25)
2321
fig = widget.figure
2422
# Need to return a copy, as original figure is too eagerley garbage
2523
# collected by the widget

0 commit comments

Comments
 (0)