From a7b09d4ce297cad9dd6fa424403c8398ec20bc74 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 11 Jan 2024 10:55:41 +0000 Subject: [PATCH 01/37] Add widgets to set bin parameters for histograms Also set np.linspace dtype based on image dtype --- src/napari_matplotlib/histogram.py | 130 +++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 14 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 2db2f08..fd44d6f 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -4,12 +4,7 @@ import numpy as np import numpy.typing as npt from matplotlib.container import BarContainer -from qtpy.QtWidgets import ( - QComboBox, - QLabel, - QVBoxLayout, - QWidget, -) +from qtpy.QtWidgets import QComboBox, QLabel, QVBoxLayout, QWidget, QGroupBox, QFormLayout, QDoubleSpinBox, QSpinBox, QAbstractSpinBox from .base import SingleAxesWidget from .features import FEATURES_LAYER_TYPES @@ -34,6 +29,50 @@ def __init__( parent: Optional[QWidget] = None, ): super().__init__(napari_viewer, parent=parent) + + # Create widgets for setting bin parameters + bins_start = QDoubleSpinBox() + bins_start.setObjectName("bins start") + bins_start.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType) + bins_start.setRange(-1e10, 1e10) + bins_start.setValue(0) + bins_start.setWrapping(True) + bins_start.setKeyboardTracking(False) + bins_start.setDecimals(2) + + bins_stop = QDoubleSpinBox() + bins_stop.setObjectName("bins stop") + bins_stop.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType) + bins_stop.setRange(-1e10, 1e10) + bins_stop.setValue(100) + bins_stop.setKeyboardTracking(False) + bins_stop.setDecimals(2) + + bins_num = QSpinBox() + bins_num.setObjectName("bins num") + bins_num.setRange(1, 100_000) + bins_num.setValue(101) + bins_num.setWrapping(False) + bins_num.setKeyboardTracking(False) + + # Set bins widget layout + bins_selection_layout = QFormLayout() + bins_selection_layout.addRow("start", bins_start) + bins_selection_layout.addRow("stop", bins_stop) + bins_selection_layout.addRow("num", bins_num) + + # Group the widgets and add to main layout + bins_widget_group = QGroupBox("Bins") + bins_widget_group_layout = QVBoxLayout() + bins_widget_group_layout.addLayout(bins_selection_layout) + bins_widget_group.setLayout(bins_widget_group_layout) + self.layout().addWidget(bins_widget_group) + + # Add callbacks + bins_start.valueChanged.connect(self._draw) + bins_stop.valueChanged.connect(self._draw) + bins_num.valueChanged.connect(self._draw) + self._update_layers(None) self.viewer.events.theme.connect(self._on_napari_theme_changed) @@ -53,11 +92,47 @@ def _update_contrast_lims(self) -> None: self.figure.canvas.draw() - def draw(self) -> None: - """ - Clear the axes and histogram the currently selected layer/slice. - """ - layer = self.layers[0] + @property + def bins_start(self) -> float: + """Minimum bin edge""" + return self.findChild(QDoubleSpinBox, name="bins start").value() + + @bins_start.setter + def bins_start(self, start: int | float) -> None: + """Set the minimum bin edge""" + self.findChild(QDoubleSpinBox, name="bins start").setValue(start) + + @property + def bins_stop(self) -> float: + """Maximum bin edge""" + return self.findChild(QDoubleSpinBox, name="bins stop").value() + + @bins_stop.setter + def bins_stop(self, stop: int | float) -> None: + """Set the maximum bin edge""" + self.findChild(QDoubleSpinBox, name="bins stop").setValue(stop) + + @property + def bins_num(self) -> int: + """Number of bins to use""" + return self.findChild(QSpinBox, name="bins num").value() + + @bins_num.setter + def bins_num(self, num: int) -> None: + """Set the number of bins to use""" + self.findChild(QSpinBox, name="bins num").setValue(num) + + def autoset_widget_bins(self, data: npt.ArrayLike) -> None: + """Update widgets with bins determined from the image data""" + + bins = np.linspace(np.min(data), np.max(data), 100, dtype=data.dtype) + self.bins_start = bins[0] + self.bins_stop = bins[-1] + self.bins_num = bins.size + + + def _get_layer_data(self, layer) -> np.ndarray: + """Get the data associated with a given layer""" if layer.data.ndim - layer.rgb == 3: # 3D data, can be single channel or RGB @@ -65,18 +140,45 @@ def draw(self) -> None: self.axes.set_title(f"z={self.current_z}") else: data = layer.data + # Read data into memory if it's a dask array data = np.asarray(data) + return data + + def on_update_layers(self) -> None: + """ + Called when the layer selection changes by ``self._update_layers()``. + """ + + if not self.layers: + return + + # Reset to bin start, stop and step + layer_data = self._get_layer_data(self.layers[0]) + self.autoset_widget_bins(data=layer_data) + + # Only allow integer bins for integer data + n_decimals = 0 if np.issubdtype(layer_data.dtype, np.integer) else 2 + self.findChild(QDoubleSpinBox, name="bins start").setDecimals(n_decimals) + self.findChild(QDoubleSpinBox, name="bins stop").setDecimals(n_decimals) + + def draw(self) -> None: + """ + Clear the axes and histogram the currently selected layer/slice. + """ + layer = self.layers[0] + data = self._get_layer_data(layer) + # Important to calculate bins after slicing 3D data, to avoid reading # whole cube into memory. if data.dtype.kind in {"i", "u"}: # Make sure integer data types have integer sized bins - step = abs(np.max(data) - np.min(data)) // 100 + step = (self.bins_start - self.bins_stop) // self.bins_num step = max(1, step) - bins = np.arange(np.min(data), np.max(data) + step, step) + bins = np.arange(self.bins_start, self.bins_stop + step, step) else: - bins = np.linspace(np.min(data), np.max(data), 100) + bins = np.linspace(self.bins_start, self.bins_stop, self.bins_num) if layer.rgb: # Histogram RGB channels independently From 96b2f0cd3cc6f5400ba4f8eaca71ea9482a56a07 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 11 Jan 2024 11:44:02 +0000 Subject: [PATCH 02/37] Add test for histogram widget when setting bin parameters --- .../tests/baseline/test_histogram_2D_bins.png | Bin 0 -> 21366 bytes src/napari_matplotlib/tests/test_histogram.py | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/napari_matplotlib/tests/baseline/test_histogram_2D_bins.png diff --git a/src/napari_matplotlib/tests/baseline/test_histogram_2D_bins.png b/src/napari_matplotlib/tests/baseline/test_histogram_2D_bins.png new file mode 100644 index 0000000000000000000000000000000000000000..eb43c3108147079f9410a40936975d94158ac6f0 GIT binary patch literal 21366 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 b-^Cj#YN-+8Uxv&S3Wb(bypnn8+Wr3z5b2@o literal 0 HcmV?d00001 diff --git a/src/napari_matplotlib/tests/test_histogram.py b/src/napari_matplotlib/tests/test_histogram.py index 1ceca51..b392ef5 100644 --- a/src/napari_matplotlib/tests/test_histogram.py +++ b/src/napari_matplotlib/tests/test_histogram.py @@ -9,6 +9,20 @@ assert_figures_not_equal, ) +@pytest.mark.mpl_image_compare +def test_histogram_2D_bins(make_napari_viewer, astronaut_data): + viewer = make_napari_viewer() + viewer.theme = "light" + viewer.add_image(astronaut_data[0], **astronaut_data[1]) + widget = HistogramWidget(viewer) + viewer.window.add_dock_widget(widget) + widget.bins_start = -50 + widget.bins_stop = 300 + widget.bins_num = 35 + fig = widget.figure + # Need to return a copy, as original figure is too eagerley garbage + # collected by the widget + return deepcopy(fig) @pytest.mark.mpl_image_compare def test_histogram_2D(make_napari_viewer, astronaut_data): @@ -20,7 +34,6 @@ def test_histogram_2D(make_napari_viewer, astronaut_data): # collected by the widget return deepcopy(fig) - @pytest.mark.mpl_image_compare def test_histogram_3D(make_napari_viewer, brain_data): viewer = make_napari_viewer() From b8623ededfa0dd4c922f507a38a9430b86665328 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 11 Jan 2024 11:50:21 +0000 Subject: [PATCH 03/37] Update changelog --- docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index cb591f9..96b353d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -7,6 +7,11 @@ Changes - Dropped support for Python 3.8, and added support for Python 3.11. - Histogram plots of points and vector layers are now coloured with their napari colourmap. - Added support for Matplotlib 3.8 +- Add widgets for setting histogram bin parameters + +Bug fixes +~~~~~~~~~ +- Use integer bin limits for integer images in ``HistogramWidget`` Bug fixes ~~~~~~~~~ From 4e4fb84b8dc7a1049dd7b1795425d06e117310f5 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 11 Jan 2024 11:52:56 +0000 Subject: [PATCH 04/37] Make linters happy --- src/napari_matplotlib/histogram.py | 24 +++++++++++++------ src/napari_matplotlib/tests/test_histogram.py | 3 +++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index fd44d6f..c527266 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -4,7 +4,17 @@ import numpy as np import numpy.typing as npt from matplotlib.container import BarContainer -from qtpy.QtWidgets import QComboBox, QLabel, QVBoxLayout, QWidget, QGroupBox, QFormLayout, QDoubleSpinBox, QSpinBox, QAbstractSpinBox +from qtpy.QtWidgets import ( + QAbstractSpinBox, + QComboBox, + QDoubleSpinBox, + QFormLayout, + QGroupBox, + QLabel, + QSpinBox, + QVBoxLayout, + QWidget, +) from .base import SingleAxesWidget from .features import FEATURES_LAYER_TYPES @@ -124,16 +134,13 @@ def bins_num(self, num: int) -> None: def autoset_widget_bins(self, data: npt.ArrayLike) -> None: """Update widgets with bins determined from the image data""" - bins = np.linspace(np.min(data), np.max(data), 100, dtype=data.dtype) self.bins_start = bins[0] self.bins_stop = bins[-1] self.bins_num = bins.size - def _get_layer_data(self, layer) -> np.ndarray: """Get the data associated with a given layer""" - if layer.data.ndim - layer.rgb == 3: # 3D data, can be single channel or RGB data = layer.data[self.current_z] @@ -150,7 +157,6 @@ def on_update_layers(self) -> None: """ Called when the layer selection changes by ``self._update_layers()``. """ - if not self.layers: return @@ -160,8 +166,12 @@ def on_update_layers(self) -> None: # Only allow integer bins for integer data n_decimals = 0 if np.issubdtype(layer_data.dtype, np.integer) else 2 - self.findChild(QDoubleSpinBox, name="bins start").setDecimals(n_decimals) - self.findChild(QDoubleSpinBox, name="bins stop").setDecimals(n_decimals) + self.findChild(QDoubleSpinBox, name="bins start").setDecimals( + n_decimals + ) + self.findChild(QDoubleSpinBox, name="bins stop").setDecimals( + n_decimals + ) def draw(self) -> None: """ diff --git a/src/napari_matplotlib/tests/test_histogram.py b/src/napari_matplotlib/tests/test_histogram.py index b392ef5..58acf23 100644 --- a/src/napari_matplotlib/tests/test_histogram.py +++ b/src/napari_matplotlib/tests/test_histogram.py @@ -9,6 +9,7 @@ assert_figures_not_equal, ) + @pytest.mark.mpl_image_compare def test_histogram_2D_bins(make_napari_viewer, astronaut_data): viewer = make_napari_viewer() @@ -24,6 +25,7 @@ def test_histogram_2D_bins(make_napari_viewer, astronaut_data): # collected by the widget return deepcopy(fig) + @pytest.mark.mpl_image_compare def test_histogram_2D(make_napari_viewer, astronaut_data): viewer = make_napari_viewer() @@ -34,6 +36,7 @@ def test_histogram_2D(make_napari_viewer, astronaut_data): # collected by the widget return deepcopy(fig) + @pytest.mark.mpl_image_compare def test_histogram_3D(make_napari_viewer, brain_data): viewer = make_napari_viewer() From 5ac1f712cfa8f760a5e4e9ad028fd8882d087ee4 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 11 Jan 2024 12:18:55 +0000 Subject: [PATCH 05/37] Fix type hints --- src/napari_matplotlib/histogram.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index c527266..3615172 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, cast +from typing import Any, Optional, Union, cast import napari import numpy as np @@ -108,7 +108,7 @@ def bins_start(self) -> float: return self.findChild(QDoubleSpinBox, name="bins start").value() @bins_start.setter - def bins_start(self, start: int | float) -> None: + def bins_start(self, start: Union[int, float]) -> None: """Set the minimum bin edge""" self.findChild(QDoubleSpinBox, name="bins start").setValue(start) @@ -118,7 +118,7 @@ def bins_stop(self) -> float: return self.findChild(QDoubleSpinBox, name="bins stop").value() @bins_stop.setter - def bins_stop(self, stop: int | float) -> None: + def bins_stop(self, stop: Union[int, float]) -> None: """Set the maximum bin edge""" self.findChild(QDoubleSpinBox, name="bins stop").setValue(stop) @@ -132,14 +132,14 @@ def bins_num(self, num: int) -> None: """Set the number of bins to use""" self.findChild(QSpinBox, name="bins num").setValue(num) - def autoset_widget_bins(self, data: npt.ArrayLike) -> None: + def autoset_widget_bins(self, data: npt.NDArray[Any]) -> None: """Update widgets with bins determined from the image data""" bins = np.linspace(np.min(data), np.max(data), 100, dtype=data.dtype) self.bins_start = bins[0] self.bins_stop = bins[-1] self.bins_num = bins.size - def _get_layer_data(self, layer) -> np.ndarray: + def _get_layer_data(self, layer: napari.layers.Layer) -> npt.NDArray[Any]: """Get the data associated with a given layer""" if layer.data.ndim - layer.rgb == 3: # 3D data, can be single channel or RGB From fab29063cdb87b848c35002847656f663ac051ef Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 11 Jan 2024 12:51:53 +0000 Subject: [PATCH 06/37] Don't allow bins lower than 0 if dtype is unisgned --- examples/histogram.py | 2 +- src/napari_matplotlib/histogram.py | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/examples/histogram.py b/examples/histogram.py index ccda491..17111f5 100644 --- a/examples/histogram.py +++ b/examples/histogram.py @@ -5,7 +5,7 @@ import napari viewer = napari.Viewer() -viewer.open_sample("napari", "kidney") +viewer.open_sample("napari", "coins") viewer.window.add_plugin_dock_widget( plugin_name="napari-matplotlib", widget_name="Histogram" diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 3615172..348a506 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -46,7 +46,7 @@ def __init__( bins_start.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType) bins_start.setRange(-1e10, 1e10) bins_start.setValue(0) - bins_start.setWrapping(True) + bins_start.setWrapping(False) bins_start.setKeyboardTracking(False) bins_start.setDecimals(2) @@ -55,6 +55,7 @@ def __init__( bins_stop.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType) bins_stop.setRange(-1e10, 1e10) bins_stop.setValue(100) + bins_start.setWrapping(False) bins_stop.setKeyboardTracking(False) bins_stop.setDecimals(2) @@ -165,13 +166,17 @@ def on_update_layers(self) -> None: self.autoset_widget_bins(data=layer_data) # Only allow integer bins for integer data + # And only allow values greater than 0 for unsigned integers n_decimals = 0 if np.issubdtype(layer_data.dtype, np.integer) else 2 - self.findChild(QDoubleSpinBox, name="bins start").setDecimals( - n_decimals - ) - self.findChild(QDoubleSpinBox, name="bins stop").setDecimals( - n_decimals - ) + is_unsigned = layer_data.dtype.kind == "u" + minimum_value = 0 if is_unsigned else -1e10 + + bins_start = self.findChild(QDoubleSpinBox, name="bins start") + bins_stop = self.findChild(QDoubleSpinBox, name="bins stop") + bins_start.setDecimals(n_decimals) + bins_stop.setDecimals(n_decimals) + bins_start.setMinimum(minimum_value) + bins_stop.setMinimum(minimum_value) def draw(self) -> None: """ From 55531747ef1e8c5597df9c081c44bc92a6dc3d3c Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 11 Jan 2024 13:12:56 +0000 Subject: [PATCH 07/37] Update changelog --- docs/changelog.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 96b353d..226cbb5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -13,10 +13,6 @@ Bug fixes ~~~~~~~~~ - Use integer bin limits for integer images in ``HistogramWidget`` -Bug fixes -~~~~~~~~~ -- Use integer bin limits for integer images in ``HistogramWidget`` - 1.1.0 ----- Additions From e86d4f692a5252881924a3e35c317d1cb24a1723 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 11 Jan 2024 13:13:17 +0000 Subject: [PATCH 08/37] Undo changes to example of HistogramWidget --- examples/histogram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/histogram.py b/examples/histogram.py index 17111f5..ccda491 100644 --- a/examples/histogram.py +++ b/examples/histogram.py @@ -5,7 +5,7 @@ import napari viewer = napari.Viewer() -viewer.open_sample("napari", "coins") +viewer.open_sample("napari", "kidney") viewer.window.add_plugin_dock_widget( plugin_name="napari-matplotlib", widget_name="Histogram" From c5e08861b18084217eb297bb042996ea46f44cc3 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 15:59:34 +0000 Subject: [PATCH 09/37] Fix autosetting bins from data --- src/napari_matplotlib/histogram.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 348a506..b0a04f1 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -135,7 +135,19 @@ def bins_num(self, num: int) -> None: def autoset_widget_bins(self, data: npt.NDArray[Any]) -> None: """Update widgets with bins determined from the image data""" - bins = np.linspace(np.min(data), np.max(data), 100, dtype=data.dtype) + if data.dtype.kind in {"i", "u"}: + # Make sure integer data types have integer sized bins + # We can't use unsigned ints when calculating the step, otherwise + # the following warning is raised: + # 'RuntimeWarning: overflow encountered in scalar subtract' + step = ( + abs(np.min(data).astype(int) - np.max(data).astype(int)) // 100 + ) + step = max(1, step) + bins = np.arange(np.min(data), np.max(data) + step, step) + else: + bins = np.linspace(np.min(data), np.max(data), 100) + self.bins_start = bins[0] self.bins_stop = bins[-1] self.bins_num = bins.size From 127d325d37133447dd9ba462c5a4a2f806ae86c4 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 16:05:24 +0000 Subject: [PATCH 10/37] remove duplicate on_update_layers method --- src/napari_matplotlib/histogram.py | 84 ++++++++++++++---------------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index b0a04f1..4371ea9 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -95,6 +95,26 @@ def on_update_layers(self) -> None: for layer in self.viewer.layers: layer.events.contrast_limits.connect(self._update_contrast_lims) + if not self.layers: + return + + # Reset to bin start, stop and step + layer_data = self._get_layer_data(self.layers[0]) + self.autoset_widget_bins(data=layer_data) + + # Only allow integer bins for integer data + # And only allow values greater than 0 for unsigned integers + n_decimals = 0 if np.issubdtype(layer_data.dtype, np.integer) else 2 + is_unsigned = layer_data.dtype.kind == "u" + minimum_value = 0 if is_unsigned else -1e10 + + bins_start = self.findChild(QDoubleSpinBox, name="bins start") + bins_stop = self.findChild(QDoubleSpinBox, name="bins stop") + bins_start.setDecimals(n_decimals) + bins_stop.setDecimals(n_decimals) + bins_start.setMinimum(minimum_value) + bins_stop.setMinimum(minimum_value) + def _update_contrast_lims(self) -> None: for lim, line in zip( self.layers[0].contrast_limits, self._contrast_lines @@ -103,6 +123,25 @@ def _update_contrast_lims(self) -> None: self.figure.canvas.draw() + def autoset_widget_bins(self, data: npt.NDArray[Any]) -> None: + """Update widgets with bins determined from the image data""" + + if data.dtype.kind in {"i", "u"}: + # Make sure integer data types have integer sized bins + # We can't use unsigned ints when calculating the step, otherwise + # the following warning is raised: + # 'RuntimeWarning: overflow encountered in scalar subtract' + step = abs(np.min(data).astype(int) - np.max(data).astype(int) // 100) + step = max(1, step) + bins = np.arange(np.min(data), np.max(data) + step, step) + else: + bins = np.linspace(np.min(data), np.max(data), 100) + + self.bins_start = bins[0] + self.bins_stop = bins[-1] + self.bins_num = bins.size + + @property def bins_start(self) -> float: """Minimum bin edge""" @@ -133,25 +172,6 @@ def bins_num(self, num: int) -> None: """Set the number of bins to use""" self.findChild(QSpinBox, name="bins num").setValue(num) - def autoset_widget_bins(self, data: npt.NDArray[Any]) -> None: - """Update widgets with bins determined from the image data""" - if data.dtype.kind in {"i", "u"}: - # Make sure integer data types have integer sized bins - # We can't use unsigned ints when calculating the step, otherwise - # the following warning is raised: - # 'RuntimeWarning: overflow encountered in scalar subtract' - step = ( - abs(np.min(data).astype(int) - np.max(data).astype(int)) // 100 - ) - step = max(1, step) - bins = np.arange(np.min(data), np.max(data) + step, step) - else: - bins = np.linspace(np.min(data), np.max(data), 100) - - self.bins_start = bins[0] - self.bins_stop = bins[-1] - self.bins_num = bins.size - def _get_layer_data(self, layer: napari.layers.Layer) -> npt.NDArray[Any]: """Get the data associated with a given layer""" if layer.data.ndim - layer.rgb == 3: @@ -166,30 +186,6 @@ def _get_layer_data(self, layer: napari.layers.Layer) -> npt.NDArray[Any]: return data - def on_update_layers(self) -> None: - """ - Called when the layer selection changes by ``self._update_layers()``. - """ - if not self.layers: - return - - # Reset to bin start, stop and step - layer_data = self._get_layer_data(self.layers[0]) - self.autoset_widget_bins(data=layer_data) - - # Only allow integer bins for integer data - # And only allow values greater than 0 for unsigned integers - n_decimals = 0 if np.issubdtype(layer_data.dtype, np.integer) else 2 - is_unsigned = layer_data.dtype.kind == "u" - minimum_value = 0 if is_unsigned else -1e10 - - bins_start = self.findChild(QDoubleSpinBox, name="bins start") - bins_stop = self.findChild(QDoubleSpinBox, name="bins stop") - bins_start.setDecimals(n_decimals) - bins_stop.setDecimals(n_decimals) - bins_start.setMinimum(minimum_value) - bins_stop.setMinimum(minimum_value) - def draw(self) -> None: """ Clear the axes and histogram the currently selected layer/slice. @@ -201,7 +197,7 @@ def draw(self) -> None: # whole cube into memory. if data.dtype.kind in {"i", "u"}: # Make sure integer data types have integer sized bins - step = (self.bins_start - self.bins_stop) // self.bins_num + step = abs((self.bins_start - self.bins_stop) // self.bins_num) step = max(1, step) bins = np.arange(self.bins_start, self.bins_stop + step, step) else: From b8ffdb79a7c20fe0ceaf7b9a0dbd8174c52c63ec Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 16:05:35 +0000 Subject: [PATCH 11/37] Make linters happy --- src/napari_matplotlib/histogram.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 4371ea9..294e509 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -125,13 +125,14 @@ def _update_contrast_lims(self) -> None: def autoset_widget_bins(self, data: npt.NDArray[Any]) -> None: """Update widgets with bins determined from the image data""" - if data.dtype.kind in {"i", "u"}: # Make sure integer data types have integer sized bins # We can't use unsigned ints when calculating the step, otherwise # the following warning is raised: # 'RuntimeWarning: overflow encountered in scalar subtract' - step = abs(np.min(data).astype(int) - np.max(data).astype(int) // 100) + step = abs( + np.min(data).astype(int) - np.max(data).astype(int) // 100 + ) step = max(1, step) bins = np.arange(np.min(data), np.max(data) + step, step) else: @@ -141,7 +142,6 @@ def autoset_widget_bins(self, data: npt.NDArray[Any]) -> None: self.bins_stop = bins[-1] self.bins_num = bins.size - @property def bins_start(self) -> float: """Minimum bin edge""" From 88760cf203f89df667bb3711a47e1a588626c5b9 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 16:21:15 +0000 Subject: [PATCH 12/37] Add HistogramWidget._bin_widgets attribute for storing bin widgets --- src/napari_matplotlib/histogram.py | 46 ++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 294e509..b846af6 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -42,7 +42,6 @@ def __init__( # Create widgets for setting bin parameters bins_start = QDoubleSpinBox() - bins_start.setObjectName("bins start") bins_start.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType) bins_start.setRange(-1e10, 1e10) bins_start.setValue(0) @@ -51,7 +50,6 @@ def __init__( bins_start.setDecimals(2) bins_stop = QDoubleSpinBox() - bins_stop.setObjectName("bins stop") bins_stop.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType) bins_stop.setRange(-1e10, 1e10) bins_stop.setValue(100) @@ -60,7 +58,6 @@ def __init__( bins_stop.setDecimals(2) bins_num = QSpinBox() - bins_num.setObjectName("bins num") bins_num.setRange(1, 100_000) bins_num.setValue(101) bins_num.setWrapping(False) @@ -84,6 +81,13 @@ def __init__( bins_stop.valueChanged.connect(self._draw) bins_num.valueChanged.connect(self._draw) + # Store widgets for later usage + self._bin_widgets = { + "start": bins_start, + "stop": bins_stop, + "num": bins_num, + } + self._update_layers(None) self.viewer.events.theme.connect(self._on_napari_theme_changed) @@ -108,12 +112,17 @@ def on_update_layers(self) -> None: is_unsigned = layer_data.dtype.kind == "u" minimum_value = 0 if is_unsigned else -1e10 - bins_start = self.findChild(QDoubleSpinBox, name="bins start") - bins_stop = self.findChild(QDoubleSpinBox, name="bins stop") - bins_start.setDecimals(n_decimals) - bins_stop.setDecimals(n_decimals) - bins_start.setMinimum(minimum_value) - bins_stop.setMinimum(minimum_value) + # Disable callbacks whilst widget values might change + for widget in self._bin_widgets.values(): + widget.blockSignals(True) + + self._bin_widgets["start"].setDecimals(n_decimals) + self._bin_widgets["stop"].setDecimals(n_decimals) + self._bin_widgets["start"].setMinimum(minimum_value) + self._bin_widgets["stop"].setMinimum(minimum_value) + + for widget in self._bin_widgets.values(): + widget.blockSignals(False) def _update_contrast_lims(self) -> None: for lim, line in zip( @@ -138,39 +147,46 @@ def autoset_widget_bins(self, data: npt.NDArray[Any]) -> None: else: bins = np.linspace(np.min(data), np.max(data), 100) + # Disable callbacks whilst setting widget values + for widget in self._bin_widgets.values(): + widget.blockSignals(True) + self.bins_start = bins[0] self.bins_stop = bins[-1] self.bins_num = bins.size + for widget in self._bin_widgets.values(): + widget.blockSignals(False) + @property def bins_start(self) -> float: """Minimum bin edge""" - return self.findChild(QDoubleSpinBox, name="bins start").value() + return self._bin_widgets["start"].value() @bins_start.setter def bins_start(self, start: Union[int, float]) -> None: """Set the minimum bin edge""" - self.findChild(QDoubleSpinBox, name="bins start").setValue(start) + self._bin_widgets["start"].setValue(start) @property def bins_stop(self) -> float: """Maximum bin edge""" - return self.findChild(QDoubleSpinBox, name="bins stop").value() + return self._bin_widgets["stop"].value() @bins_stop.setter def bins_stop(self, stop: Union[int, float]) -> None: """Set the maximum bin edge""" - self.findChild(QDoubleSpinBox, name="bins stop").setValue(stop) + self._bin_widgets["stop"].setValue(stop) @property def bins_num(self) -> int: """Number of bins to use""" - return self.findChild(QSpinBox, name="bins num").value() + return self._bin_widgets["num"].value() @bins_num.setter def bins_num(self, num: int) -> None: """Set the number of bins to use""" - self.findChild(QSpinBox, name="bins num").setValue(num) + self._bin_widgets["num"].setValue(num) def _get_layer_data(self, layer: napari.layers.Layer) -> npt.NDArray[Any]: """Get the data associated with a given layer""" From d56942b72abd8e65f821ba07feb99ff6f0a25526 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 16:33:50 +0000 Subject: [PATCH 13/37] Fix calculation of bins from widget values --- src/napari_matplotlib/histogram.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index b846af6..3215b14 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -136,12 +136,7 @@ def autoset_widget_bins(self, data: npt.NDArray[Any]) -> None: """Update widgets with bins determined from the image data""" if data.dtype.kind in {"i", "u"}: # Make sure integer data types have integer sized bins - # We can't use unsigned ints when calculating the step, otherwise - # the following warning is raised: - # 'RuntimeWarning: overflow encountered in scalar subtract' - step = abs( - np.min(data).astype(int) - np.max(data).astype(int) // 100 - ) + step = abs(np.max(data) - np.min(data)) // 100 step = max(1, step) bins = np.arange(np.min(data), np.max(data) + step, step) else: @@ -213,7 +208,7 @@ def draw(self) -> None: # whole cube into memory. if data.dtype.kind in {"i", "u"}: # Make sure integer data types have integer sized bins - step = abs((self.bins_start - self.bins_stop) // self.bins_num) + step = abs(self.bins_stop - self.bins_start) // self.bins_num step = max(1, step) bins = np.arange(self.bins_start, self.bins_stop + step, step) else: From 11590b251846ab9b6023663bacb6d286c3e5a15f Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 16:51:22 +0000 Subject: [PATCH 14/37] Calculate step using n_bins-1 n_bins corresponds to number of bin edges rather than bins --- src/napari_matplotlib/histogram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 3215b14..4ce6ebc 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -208,7 +208,7 @@ def draw(self) -> None: # whole cube into memory. if data.dtype.kind in {"i", "u"}: # Make sure integer data types have integer sized bins - step = abs(self.bins_stop - self.bins_start) // self.bins_num + step = abs(self.bins_stop - self.bins_start) // (self.bins_num - 1) step = max(1, step) bins = np.arange(self.bins_start, self.bins_stop + step, step) else: From 08a5086f345e5db2418f2f4e1f5f4e5b169567a4 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 16:52:16 +0000 Subject: [PATCH 15/37] cannot use negative start bin for uint data --- .../tests/baseline/test_histogram_2D_bins.png | Bin 21366 -> 19894 bytes src/napari_matplotlib/tests/test_histogram.py | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/napari_matplotlib/tests/baseline/test_histogram_2D_bins.png b/src/napari_matplotlib/tests/baseline/test_histogram_2D_bins.png index eb43c3108147079f9410a40936975d94158ac6f0..db401612c44fb7eb92dc1ae9f2fd66a7bbd69fd6 100644 GIT binary patch literal 19894 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 BUXB0& literal 21366 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 b-^Cj#YN-+8Uxv&S3Wb(bypnn8+Wr3z5b2@o diff --git a/src/napari_matplotlib/tests/test_histogram.py b/src/napari_matplotlib/tests/test_histogram.py index 58acf23..399db3d 100644 --- a/src/napari_matplotlib/tests/test_histogram.py +++ b/src/napari_matplotlib/tests/test_histogram.py @@ -17,8 +17,8 @@ def test_histogram_2D_bins(make_napari_viewer, astronaut_data): viewer.add_image(astronaut_data[0], **astronaut_data[1]) widget = HistogramWidget(viewer) viewer.window.add_dock_widget(widget) - widget.bins_start = -50 - widget.bins_stop = 300 + widget.bins_start = 0 + widget.bins_stop = 350 widget.bins_num = 35 fig = widget.figure # Need to return a copy, as original figure is too eagerley garbage From 65e84f8692e7ad42586304a643621b02dab95670 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 16:56:00 +0000 Subject: [PATCH 16/37] Make HistogramWidget bins_num widget correspond to number of bins rather than number of bin edges --- src/napari_matplotlib/histogram.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 4ce6ebc..728dfc8 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -148,7 +148,7 @@ def autoset_widget_bins(self, data: npt.NDArray[Any]) -> None: self.bins_start = bins[0] self.bins_stop = bins[-1] - self.bins_num = bins.size + self.bins_num = bins.size - 1 for widget in self._bin_widgets.values(): widget.blockSignals(False) @@ -208,11 +208,13 @@ def draw(self) -> None: # whole cube into memory. if data.dtype.kind in {"i", "u"}: # Make sure integer data types have integer sized bins - step = abs(self.bins_stop - self.bins_start) // (self.bins_num - 1) + step = abs(self.bins_stop - self.bins_start) // (self.bins_num) step = max(1, step) bins = np.arange(self.bins_start, self.bins_stop + step, step) else: - bins = np.linspace(self.bins_start, self.bins_stop, self.bins_num) + bins = np.linspace( + self.bins_start, self.bins_stop, self.bins_num + 1 + ) if layer.rgb: # Histogram RGB channels independently From 8a7d9e46da49492172df8f36e0f579c995407a65 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 17:07:02 +0000 Subject: [PATCH 17/37] fix typo in comment about using 128 bins for float data --- src/napari_matplotlib/histogram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 2329c1a..39bcfa4 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -31,7 +31,7 @@ def _get_bins(data: npt.NDArray[Any]) -> npt.NDArray[Any]: step = np.ceil(np.ptp(data) / 100) return np.arange(np.min(data), np.max(data) + step, step) else: - # For other data types, just have 128 evenly spaced bins + # For other data types, just have 99 evenly spaced bins return np.linspace(np.min(data), np.max(data), 100) From 7c4cdc87328480e53bd07847e79fcf27b9fef697 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 17:10:07 +0000 Subject: [PATCH 18/37] Update changelog --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 673a0ef..f4caaf3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -20,6 +20,7 @@ Other changes - The ``HistogramWidget`` now has two vertical lines showing the contrast limits used to render the selected layer in the main napari window. - Added an example gallery for the ``FeaturesHistogramWidget``. +- Add widgets for setting bin parameters for ``HistogramWidget``. 1.2.0 ----- @@ -28,7 +29,6 @@ Changes - Dropped support for Python 3.8, and added support for Python 3.11. - Histogram plots of points and vector layers are now coloured with their napari colourmap. - Added support for Matplotlib 3.8 -- Add widgets for setting histogram bin parameters 1.1.0 ----- From 6261f4c36fb531146cb270f3ad2bf9fc5d62ac1c Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Wed, 14 Feb 2024 10:54:40 +0000 Subject: [PATCH 19/37] Add 'num_bins, 'start', and 'stop' parameters to '_get_bins' --- src/napari_matplotlib/histogram.py | 56 ++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index bec5c61..5036071 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -27,15 +27,44 @@ _COLORS = {"r": "tab:red", "g": "tab:green", "b": "tab:blue"} -def _get_bins(data: npt.NDArray[Any]) -> npt.NDArray[Any]: +def _get_bins( + data: npt.NDArray[Any], + num_bins: int = 100, + start: Optional[Union[int, float]] = None, + stop: Optional[Union[int, float]] = None, +) -> npt.NDArray[Any]: + """Create evenly spaced bins with a given interval. + + If `start` or `stop` are `None`, they will be set based on the minimum + and maximum values, respectively, of the data. + + Parameters + ---------- + data : napari.layers.Layer.data + Napari layer data. + num_bins : integer, optional + Number of evenly-spaced bins to create. + start : integer or real, optional + Start bin edge. Defaults to the minimum value of `data`. + stop : integer or real, optional + Stop bin edge. Defaults to the maximum value of `data`. + + Returns + ------- + bin_edges : numpy.ndarray + Array of evenly spaced bin edges. + """ + start = np.min(data) if start is None else start + stop = np.max(data) if stop is None else stop + if data.dtype.kind in {"i", "u"}: # Make sure integer data types have integer sized bins - step = np.ceil(np.ptp(data) / 100) - return np.arange(np.min(data), np.max(data) + step, step) + step = np.ceil((stop - start) / num_bins) + return np.arange(start, stop + step, step) else: - # For other data types, just have 100 evenly spaced bins - # (and 101 bin edges) - return np.linspace(np.min(data), np.max(data), 101) + # For other data types we can use exactly `num_bins` bins + # (and `num_bins` + 1 bin edges) + return np.linspace(start, stop, num_bins + 1) class HistogramWidget(SingleAxesWidget): @@ -217,15 +246,12 @@ def draw(self) -> None: # Important to calculate bins after slicing 3D data, to avoid reading # whole cube into memory. - if data.dtype.kind in {"i", "u"}: - # Make sure integer data types have integer sized bins - step = abs(self.bins_stop - self.bins_start) // (self.bins_num) - step = max(1, step) - bins = np.arange(self.bins_start, self.bins_stop + step, step) - else: - bins = np.linspace( - self.bins_start, self.bins_stop, self.bins_num + 1 - ) + bins = _get_bins( + data, + num_bins=self.bins_num, + start=self.bins_start, + stop=self.bins_stop, + ) if layer.rgb: # Histogram RGB channels independently From 426a0f5f8401300b1c8c0e93c479e5559e1dec97 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Sat, 25 May 2024 10:36:31 +0100 Subject: [PATCH 20/37] use '| None' rather than Optional[Union[...]] for type hints --- src/napari_matplotlib/histogram.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 042ef8f..fe281c4 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Union, cast +from typing import Any, cast import napari import numpy as np @@ -30,8 +30,8 @@ def _get_bins( data: npt.NDArray[Any], num_bins: int = 100, - start: Optional[Union[int, float]] = None, - stop: Optional[Union[int, float]] = None, + start: int | float | None = None, + stop: int | float | None = None, ) -> npt.NDArray[Any]: """Create evenly spaced bins with a given interval. @@ -195,7 +195,7 @@ def bins_start(self) -> float: return self._bin_widgets["start"].value() @bins_start.setter - def bins_start(self, start: Union[int, float]) -> None: + def bins_start(self, start: int | float) -> None: """Set the minimum bin edge""" self._bin_widgets["start"].setValue(start) @@ -205,7 +205,7 @@ def bins_stop(self) -> float: return self._bin_widgets["stop"].value() @bins_stop.setter - def bins_stop(self, stop: Union[int, float]) -> None: + def bins_stop(self, stop: int | float) -> None: """Set the maximum bin edge""" self._bin_widgets["stop"].setValue(stop) From 67a864101d2d7d037bfbe8f9f3e1b55569cd125e Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Sat, 25 May 2024 11:02:24 +0100 Subject: [PATCH 21/37] remove widgest to set start and stop values for histogram bins --- src/napari_matplotlib/histogram.py | 145 +++--------------- .../tests/baseline/test_histogram_2D_bins.png | Bin 19894 -> 19832 bytes src/napari_matplotlib/tests/test_histogram.py | 4 +- 3 files changed, 24 insertions(+), 125 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index fe281c4..aeef841 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -7,9 +7,7 @@ from napari.layers import Image from napari.layers._multiscale_data import MultiScaleData from qtpy.QtWidgets import ( - QAbstractSpinBox, QComboBox, - QDoubleSpinBox, QFormLayout, QGroupBox, QLabel, @@ -30,41 +28,29 @@ def _get_bins( data: npt.NDArray[Any], num_bins: int = 100, - start: int | float | None = None, - stop: int | float | None = None, ) -> npt.NDArray[Any]: """Create evenly spaced bins with a given interval. - If `start` or `stop` are `None`, they will be set based on the minimum - and maximum values, respectively, of the data. - Parameters ---------- data : napari.layers.Layer.data Napari layer data. num_bins : integer, optional - Number of evenly-spaced bins to create. - start : integer or real, optional - Start bin edge. Defaults to the minimum value of `data`. - stop : integer or real, optional - Stop bin edge. Defaults to the maximum value of `data`. + Number of evenly-spaced bins to create. Defaults to 100. Returns ------- bin_edges : numpy.ndarray Array of evenly spaced bin edges. """ - start = np.min(data) if start is None else start - stop = np.max(data) if stop is None else stop - if data.dtype.kind in {"i", "u"}: # Make sure integer data types have integer sized bins - step = np.ceil((stop - start) / num_bins) - return np.arange(start, stop + step, step) + step = np.ceil(np.ptp(data) / num_bins) + return np.arange(np.min(data), np.max(data) + step, step) else: # For other data types we can use exactly `num_bins` bins # (and `num_bins` + 1 bin edges) - return np.linspace(start, stop, num_bins + 1) + return np.linspace(np.min(data), np.max(data), num_bins + 1) class HistogramWidget(SingleAxesWidget): @@ -82,53 +68,28 @@ def __init__( ): super().__init__(napari_viewer, parent=parent) - # Create widgets for setting bin parameters - bins_start = QDoubleSpinBox() - bins_start.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType) - bins_start.setRange(-1e10, 1e10) - bins_start.setValue(0) - bins_start.setWrapping(False) - bins_start.setKeyboardTracking(False) - bins_start.setDecimals(2) - - bins_stop = QDoubleSpinBox() - bins_stop.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType) - bins_stop.setRange(-1e10, 1e10) - bins_stop.setValue(100) - bins_start.setWrapping(False) - bins_stop.setKeyboardTracking(False) - bins_stop.setDecimals(2) - - bins_num = QSpinBox() - bins_num.setRange(1, 100_000) - bins_num.setValue(101) - bins_num.setWrapping(False) - bins_num.setKeyboardTracking(False) + num_bins_widget = QSpinBox() + num_bins_widget.setRange(1, 100_000) + num_bins_widget.setValue(101) + num_bins_widget.setWrapping(False) + num_bins_widget.setKeyboardTracking(False) # Set bins widget layout bins_selection_layout = QFormLayout() - bins_selection_layout.addRow("start", bins_start) - bins_selection_layout.addRow("stop", bins_stop) - bins_selection_layout.addRow("num", bins_num) + bins_selection_layout.addRow("num bins", num_bins_widget) # Group the widgets and add to main layout - bins_widget_group = QGroupBox("Bins") - bins_widget_group_layout = QVBoxLayout() - bins_widget_group_layout.addLayout(bins_selection_layout) - bins_widget_group.setLayout(bins_widget_group_layout) - self.layout().addWidget(bins_widget_group) + params_widget_group = QGroupBox("Params") + params_widget_group_layout = QVBoxLayout() + params_widget_group_layout.addLayout(bins_selection_layout) + params_widget_group.setLayout(params_widget_group_layout) + self.layout().addWidget(params_widget_group) # Add callbacks - bins_start.valueChanged.connect(self._draw) - bins_stop.valueChanged.connect(self._draw) - bins_num.valueChanged.connect(self._draw) + num_bins_widget.valueChanged.connect(self._draw) # Store widgets for later usage - self._bin_widgets = { - "start": bins_start, - "stop": bins_stop, - "num": bins_num, - } + self.num_bins_widget = num_bins_widget self._update_layers(None) self.viewer.events.theme.connect(self._on_napari_theme_changed) @@ -144,27 +105,9 @@ def on_update_layers(self) -> None: if not self.layers: return - # Reset the bin start, stop and step values based on new layer data + # Reset the num bins based on new layer data layer_data = self._get_layer_data(self.layers[0]) - self.autoset_widget_bins(data=layer_data) - - # Only allow integer bins for integer data - # And only allow values greater than 0 for unsigned integers - n_decimals = 0 if np.issubdtype(layer_data.dtype, np.integer) else 2 - is_unsigned = layer_data.dtype.kind == "u" - minimum_value = 0 if is_unsigned else -1e10 - - # Disable callbacks whilst widget values might change - for widget in self._bin_widgets.values(): - widget.blockSignals(True) - - self._bin_widgets["start"].setDecimals(n_decimals) - self._bin_widgets["stop"].setDecimals(n_decimals) - self._bin_widgets["start"].setMinimum(minimum_value) - self._bin_widgets["stop"].setMinimum(minimum_value) - - for widget in self._bin_widgets.values(): - widget.blockSignals(False) + self._set_widget_nums_bins(data=layer_data) def _update_contrast_lims(self) -> None: for lim, line in zip( @@ -174,50 +117,10 @@ def _update_contrast_lims(self) -> None: self.figure.canvas.draw() - def autoset_widget_bins(self, data: npt.NDArray[Any]) -> None: - """Update widgets with bins determined from the image data""" + def _set_widget_nums_bins(self, data: npt.NDArray[Any]) -> None: + """Update num_bins widget with bins determined from the image data""" bins = _get_bins(data) - - # Disable callbacks whilst setting widget values - for widget in self._bin_widgets.values(): - widget.blockSignals(True) - - self.bins_start = bins[0] - self.bins_stop = bins[-1] - self.bins_num = bins.size - 1 - - for widget in self._bin_widgets.values(): - widget.blockSignals(False) - - @property - def bins_start(self) -> float: - """Minimum bin edge""" - return self._bin_widgets["start"].value() - - @bins_start.setter - def bins_start(self, start: int | float) -> None: - """Set the minimum bin edge""" - self._bin_widgets["start"].setValue(start) - - @property - def bins_stop(self) -> float: - """Maximum bin edge""" - return self._bin_widgets["stop"].value() - - @bins_stop.setter - def bins_stop(self, stop: int | float) -> None: - """Set the maximum bin edge""" - self._bin_widgets["stop"].setValue(stop) - - @property - def bins_num(self) -> int: - """Number of bins to use""" - return self._bin_widgets["num"].value() - - @bins_num.setter - def bins_num(self, num: int) -> None: - """Set the number of bins to use""" - self._bin_widgets["num"].setValue(num) + self.num_bins_widget.setValue(bins.size - 1) def _get_layer_data(self, layer: napari.layers.Layer) -> npt.NDArray[Any]: """Get the data associated with a given layer""" @@ -248,9 +151,7 @@ def draw(self) -> None: # whole cube into memory. bins = _get_bins( data, - num_bins=self.bins_num, - start=self.bins_start, - stop=self.bins_stop, + num_bins=self.num_bins_widget.value(), ) if layer.rgb: diff --git a/src/napari_matplotlib/tests/baseline/test_histogram_2D_bins.png b/src/napari_matplotlib/tests/baseline/test_histogram_2D_bins.png index db401612c44fb7eb92dc1ae9f2fd66a7bbd69fd6..98e3cde1254b75ffc92e7786f8106fe4cce3c16f 100644 GIT binary patch literal 19832 zcmeIaXH-*NyEYm{1q+G|6{M(i1e9K+C<2Bm9YPb3PCz<@Y6BGkMT*jU=t!5YAfWV? z1PE0`dM9+qH&^f}@3Z%NzOv65;~Qt}9}Y~i%3O2a^DftYUF+q2WjV@YG{;~t7$yAP zT{Retj1dMqs6%lCd=usR}Kp}22(P2eq?L!Y-?r0=xXNZWMyy1 z$1Tjwd!5nJ+4+%^C=USa3^wb-2)(H^zcXyqsa^QY zDi^ENPl*TRJuBFntF(d+9@M1%Qo?;$mEz64*9Q-To{$#wS3L7;o%{w;vG%HnMMPy> zh#Z5!@QV0wyO5W-@Pdfmv-XLVqhM4pMXF1ar2k9`Q!tXgkcJ%qe;?2sq<}tl-;K8$ zn;)zo1Pl`mjG?+!!lmI zd}$>Zft$hnsOPb6ofgQ<$k5VikBjn}cu0AfKV8P)TEii5bt?DMNb$onPuGWW?LLBf zeSKB@CSNJ~OlSLRd~3WKy~{oO`Qd5~s&CU2KV-jtePYmkOgLOS%%-m^wftw7f`I+d z+13Wn#V_PV#=9t`_cv6nrmRpl7>>DtGWGTrUoEmDCl=TFOcbFf`I3l%scCDY7rZ=^ zwg@wM0Sq2@-@eT;%5Yi8*5%;l#)hUUfG6ODqgH~1R`J7ovMRThQjQq-lrd#|{CNM} zLZ!5uN;3{wI>-Xg%*(^2;A>a|>Jpy^8s2dJ;|33xN{ek=HWrPQ;&^4=zJ0sweoNG6 z$GuR(#l>Z7vF;Q62Y%u$y?nY@@vNbhaQ`p|)Z3!ajc2S<3!u z?~0Rjq7QrDLj!*zmv!K0a)^Y6t!*|32ghoxy{{fF_;O-raxP}!SDU7{cNLRWdz`qn zwYA!Ugm=`OO}i2Zw+TUf8heHAPcyt-$a83-hJZ85!x{N1g-C3@VER6u8OFpc8q2lj#v(eNnY~J!b<}L? zL)5%OhO=(d-28o|Y4wk{?a@(siDOqKk$pl<3 zq0ZXYRzu09xU0e~a+fpj>qa?ZcQy9?`}B56qW%R@Zd>g9BVi`EfB;5bsr4DwNUL`~S+^bec_*B=>{)fYolRXfJ7 z;V{;)slkd6iJe7hO%1K|ZJ7(3c$Gozw{J%7o13R4B_$Pk;<$q@-mD!)Cnnymudk<( zF!@x&B_^h)S8AtfU|>)f-EiD(cF{ia;kWlWWsDMrT`wi?H9o&k(swFxrb=mHI`azd z5Q^po#SCq=4t}v=%by1;=v8)#0W0RKH%8nUphr+L*2$f~De&yda1QAw+Ix-h`ff+t zS7!jbGC+N2eiazlZAM0J-5MpZ_2raL!uxku9<)wlmfeFncov3@jdcv;BDf8bo;*27 z6cej7%+tz2WDE>c%PT8KFiAJq5+Wr$`ohO*k7JnGrv`{URIAy7oHn!#I>U?g{r*B& zA+P+B(L8Z`JxiAq!z$FwR=#PmWBoA!0fDI>3fx$1j$X0Ppy;5_HWRb!)Kz%YEst08 zQKB-No83`W#aa{w(Cz z{oNoPt@^8a+niHGL^n$-x2IykP6L-~AYj^{!f*WJu5bKMmFLWNbR4$!aQn;$i#RPs z#n%nNRkzjA%E+5TJU&YbT*~vA0ybJ%X=$>$y1FQ@d3^(~CFa(xOTW#z!yNW9I2aq^ zvue6KDJLm02Lo=Ta_8R`24)nrU!v3P*iFe-KBU|K5JM-CVu$h z3j(E4HqzbHyY7Nbpwn&GZJi%HwZGS_GQWB>u(-aA(GQ#lyFrwu3G9+YNg*3K1V;z5EkVwVe2Qh*8GeheMz_Xc}kD@`0165c*` zbXBAOoN+0q&G@l9FGvA|!Xe16)Qdhu?Uw|%jk$ROy8B6K%3ZVdR;^^b672K=$n6`~ zMtxxS9Ik#GccjfPx&u6{AC)Qsk@?~U#qJ2X>uA=QEhWa?12)cn}Sd0*5S*5~<2Ie;efMBdno zVh({XcJ6fGO2KUXqxYW;<`!(5lcPA0uRh|>&pnD+7ru1q((PYVQym8$&i7y4 zP0sFI6m?Yy8;TPMqXN_Y0Hz@Ssz59EelnhJGNXvSfAkGAGjm5b#W6ZjKgIGNS9i6o zg`{n~v<$O8G)qQQw2`dm#tUZV=chKVBuSIM`RN|LmFjwOl^+b&@8nbT11P}%`4Ink z*#FOM;~&qTL?#YhYQ&2!U7XRaO01*X@?G7p`=y`k9>>YvCz&~83ivI-;qcf`tJOo` z!3*T@nm2O&sIHRIi#stDLtHArz9~HvKg9BpiN{jQXUg+ibYCu;89YQqS_CQbI_u219jF5ZRw{_&9aC zcAg=MxVs+y<_#kpvLXS;pZCCyno0`fJ~qqLelU_WgI7jWe7D{)P1Wqp)a;B(hh}SK z(RBQ9s|x?L?ZwLqP)8?z^`lr8E=sydjnY%!d@Ykn`kyrMO$VJe7-2 zw$|pd?7e673UsccR$7HHWlk1gp4fPKH`;mjoeZ=d2gRRj6;D)BU)M>H@ZPfQOp-x- zdXyNWjJB)ZG#B5ROqN$rP}kDZs=)9bBkhbUt;SB@=#jV>C8vYp;evh1bB?{2C&ZN> zK1`V}o1h{)YHp~iHTXsHRj(p2Lv!3j*HqPTT-|BZA%m4ZC{>}hlhpr z%&KJpTe1Gt@*y+@gtyH~n~OovZ2G;yapme&czr(Qsv=GZ1F&}g(F*5ObzN>y*Es~8gqKcdXXi8~E7H8>ec}f` z3y!N-ul{8mS7l#cOgw!}7P!F)=8gF7!{7ppw*O1pP}($8pNX*zm`MBb63tt-GyvO>iy)0b7-H8SSThO z5K1J2ros}$P$+qG9({g+Lr_pl+sL~Zn`;0zUF^QkrnnjJyH}Q5xhxOOlzN&XG?#=t z-(3Z0UYEz4$;7L=f6wc$LAPNHo!da7+pxEGi1^z3y^Tgm>+UqASFd09^kk@O8W_+` zvX}hc3GYGBnt4o4Z;5s7GmD(67~ahdzATc2GxX$=;uGWBH*6E`s#P_EM_&$e^#@SW zvcUVxU9!IOiFzU>4%;+Ga7En|ufZUXU#Lp(+1=Xo-CONeA?&QQXIQS=)i*R)Z!C^R z-7?EGF)@jnn;@Wy-IoFpg{UBF>&$RWA(~}aM3;=5(pt6d#Z53jevjV7#0*$_f*E0k z1D|59JP{MKbhENFm>~~Xt2xrj%4*M-o13Fc#avNaR@OgFB|dTal_Qs4xrXQZTz931 z3O{}Udka$1#RCj8907tKJXsX zCz9nU6^zAXn1~M*DY#0w|WJrLhp^yU~3T)PXX6eSI7p+{>ls&!1-(7q8My zf0Gh3pbsiQhy2iY?_B)2}Fr=#(ecQHBn%$LTg(a zR61yAYvX3qmTKw5cJR!Xc+K^&az5F+jFB(k8-b{bbxbNOU|yd)2X*8W^N zC`6)AxWPT2cdRO%2I^=wN#7{DN{1L?&iKfMNMJN17YQ6nKcXr8X(~^eHsXODMPVG~tX7#;Y(+$-=Bnp5V``zy}hh1`P6+l$D z=0x!5rInY9GBXMDgP^~=C?~0fKt$CTEeur=eyXe3j6RtlwD75Lg2KYIaVQ}}9bIy9 zIr)%MXDSL%(41I3S1Zd%ynd7sjGw~#<;#}g z^())Wo1LrWq!j#6F~Fj1CfZ^%v$IdEYShY`m?V|EE!d$9NMYiMA*i3omW?mXg^n+mMvScy(tynoFPpt^ulxS#Cm=g*$)&W`{0K&JR_AXCS}F)W_pt3g{G})(BRf6hX<>ygLHa`x2gbPA zHhtooQ+u2CaJ4tadFNAR@MWtKBuKr>N1nL1`mTw9v_?L(++{9pcyCQ;Yh-s_U~;*c z8UFnFbJWCKsmpqi*PtsN8JaSaOPdTY^;J0@=c^3zqX&~D6y@N;YYQofJ@+g@+unpr zk{|j^^!~RKfL!T-lb3#jiI4hWT|GT1tnprIkSEE`&Zfq%{}tSIE9y;DU0q9Fy$akx z%E-vbtEkvhb1<>93TcOG=lJdYIZe-{r$1q@K6F2<)BQB>N3Ln7#2WFga+EfQ8&j} zTe(e_v7Vs#N+BaZ$p-92*&>N4qifvyrvJZ>mWj0nh+^V{!V&-$g%4(}TdhO6cde2` zr``Drb9UHYIrf`o#7%1fKMf6yNs!K4pMFZ@!S_bWaA? z*4~c`H9bFm<{}Esivczs79O73IMIfwzhGptvA+p;YA04U4~icxj4hAIX~byL_?-Gi z`&>_8tlbP{uJ+2Vh4~zqE=K2=tpTr@uM6o4YHAg)J^C-4h5$1u&;4rWe*&b$tb0n` z(M_KDcuUOnBBHLsz9xl2+6J5tON6i)kZfp{n*=2aaPX?965T?g2JEC~NWUh4UwgB1 zH5yJW@E7@;&Pb-tsj1JY{_WRq$3>fmO7jv0lGeTLCO4olv5k_dN?6+e{ciXlu;YIZ z_`jZoL``C86y)Vs10;70E8G^a#*qrKqAqHnM6GthySWfB+CnpW0B5y`^FYM=EWb=j zP8M)qd@!yOeP*#Mzl~#=K5){wb-Nk^oS|5zjQ1Z6yPP9$Aoo1k znM}iH{`$<>vo5o}H<1G$Z?6+=OXj1+^~M?oncy5eJlcCjq>kWs^|WtJJ;|vGk(TXo zXR1EA=Yz7$x8(vgR~OW<5E^h#1}IpOM>osvbYj4{)29>PUdto=y=&(ZKqVE1(eSTt_Ly;oCom+AEs~ixkoau$zAnDcB0f5X2e5M|pW=H6${p207+WN#ljl z+)H4D1NW|hLb>JWm9`C+Sanx`#!^pf>A(;Bz!xt%l!S(}mzN!p!_|Ww5Oe@L6wrNy zDx@*_nq06YvDzC^ZTn$}I3Y<0ve##s`g)0?6rTeiw4z=fAtAYX8JWHKz`$1zOYz}v z-*#3SDd+lDsO~ov=yMF8xHJ{6Ok^dDxWLr_aF%;XRAYT-6&=c?ZI;UPeag9{w|z=c z!^Nc-G;Bba)8~!^MFxPZqSh4@!vOBFtZ%_{1{qm`uJ@z;QR@N zee2&RR=%4T!b53jD!JBHP%1^S)24-eJ4lOZ+|cg_K#P+tYt}6<^RF*8LjAejNYOI= zTIf!71|4Y z^T)x&K6?jBNKi=*X1D4MFCQnaxF{d^*<+?u?BX)*(?5r)Z zUA)k9s#R!M!^$cH)a9f0!>T%L@V#n-a%Zdcn<{7pR8+Odayj1`DFPeV7=6_ai<}7` zSHKr5XGK0J8w}7Z5)c%$CQM^M3Ae|pLP;BeuBlm%1AW<_3+tV73|0$6JmHZ-1H*z3 zzfsA^$;}jN8xcx3y(G?8?nRY5PbK;6Zg1m%S4@vA2`9ddg-GTmySUIiTB@H8?Ul)` z>mCu8wUjOKg%3x+@8C_28_h&Lc_0f%z zK<57N4=QNh6-Q6f%Z>WTwLzLpif0^m8(YJ)*l%#==$^2tcFVLuBwlZkF#@Tq4fg{Pymw0g!ryX zbD>5Lo0*%#Lz|kL!zJfPF!*v9`|@RiCAe^ZA^I*4x8e1``X3A{&t(EZhuF81QfMeW zCgw77s0dzzn!F-&_pas7Z#KKTulFe$j{rqOac9kKbws+xdd=AP>x;KrdntHAm(gAl zIlb4t(A5b^BAh-%F3^`q?lRY}85;I3I?L01?{1b>PR}O?A$TaDuN2w#sSP?dm#%i} z7Bazw1|B6ZqX%;);ysl;Jn8`MYxcVql+IfBTU{8g(cO5HvbySQwmO;E#Z$dl3Z)uP z^U9k5NNoiIlf248?mmqMiP8o!t$Z^=ZwCpww&hd5qBRW-b3x~aR+I&@%8^G{p~P)3 z3Ba7tx--!hsvPt9QN`eNH(DjIx~xougPUWSsIRZVR56(Q`t|EyyN-^#_#hf+(@HH0 z&%g>?=V$r9t^dX>57V_i@MK-uCfK4KJ@sqV2Qpsh$Wfy`L+aQ3kv&)1g^k-EaG%T==|X#}cgkU8wyu6g%YxaGy6wyX#sAM9J> zHYjHU^o2?(byQ1T{T&3LLR=`H-I)MPo)J;Yjb8A&im&g2IGw2Tx8b$_#VGv8 z`gcrUdSfxsj{DUb?Sj$N&MHqs*kH5mO1xJm&stF0#q+~waCzOYr#5LMeV*K+4~e+$ z0SF?xTfYdn?QK?OW~)DmIM6D=g23VcVAD-@N}^}>`QzW)Qn-jl>XjsC z1XI=ekU9AGiyE(T_|1Pk*karLZfy_fo_h1)Lk4S{*YxXM0AIUor@+g-yHfo23+~0X zEtzMK#XYM2Kk&dxSv*&P`~PLd#;Fc*4Xlc2?cwkQG>q{D)X%0!@Gsz{{ISwRdP;X1sg(6`w|ab*0#Q& z_OnD6Fs2i946wE%Aa?q}M0JcZJm+7l?Y4<@DUZ$Ux4s)L9P|@}#zc{DEl418aUKi1TNWp1Bu#)jXrJT(wMj zOyK|pi3D>n{YSb+sYS&6?)5u}n!ZI8V4m!)Cf{~!Ku~7o=RYj{m79IPxbay68RO6W zCzwXTC9=#7e|3N3RB}IcFIzL?sH?P1n1(JBeA{L{tHX@1h4XyoQd^1B{c<*evV!uF(g@ly*Lo z!^++}ZCH=Legt>GZ$t{RrStQmdABH)P}V={r$3yt(1|s`3r&5gTIpf_ryB{aKMK$Yu*q0OXc^D7*Jvj5v9!4Frj`7#m=;C|7DrGLfV6f4bFq2ST`?6yO z&;5gp3F}?c94jj7iR`=AgH|{#%BZcxIs?SV+F$feh z8tZ^zN!W@V?0X63+fP`@&=gpXIkV z*-+*gV~?LY)kFWLVhHI@$0uTY(j{<)deRDHp&SP(sQfSIYxQxdMwRWD*4ele+_fGJ zQ42q3KVZZrUri9LrZ0Od*pm`S3G#Fk?` zJqH_R&}N=*hyC`NWd(JxKARkCK%z^Fnwn;kNc6971`8ndhKZg7w@gzzJL_*mqp7BO z_4@|;5L`d^P=e`FMA${cYx_z5qUdLBFn2LR$?<0n!RTa)>Jlj`3;-!kuu|)fO#cQc z`DVPQqox*C?D(8!utCDP?r&-SXJ;tU_G2`G!>^oU%wB?=01VLePP~B2XVUao`Gy;h zEw~6=6chwscNkJI9&vJh6`IqT)wiH2zkn1h(Rrr5w8my?1D!VU7-sS@tk<$?v2E;? z_m+rDQxk8tGFu!OvSo$wzF$o@#Q0j=IYnTH&L$IwI5RSFOa5(ONjvr^ z7+LNuUsm<+X7S8)@P*5|7O;1j3@{sx+7eHP$hP6xRHWaH7Lz*HIsRZ1N;!T-n3dkO z0!)-39_pJ434F-~n@YedNHAg+I}m<8bL}GW@EN}~0H7&@y^Z!c5|?_gL!#xDO$J5Ytr}1L zRL_u$dJT5kMRmrOl!d7*y_Ce-)RZO@fSJCAZ1dTMEnd@OqCX?5OfL+KM) z*J9nb1s1$v9jcIXx$=FoFoJSB_1ja;f|RuH0CzQZfh8o5%0PSj&lX~oAkP<3)M|PJ zw{8;MbFrerkM{u$c5Abps4!f)w?*x(pr8P?TxYsfBvhT9i;kS4&)%8h`?=9X>Cza& zfa^+CSlzP81L+#zKNt*G?7A9w&atWQ12Q@w?}Dfn?Zlni-@kvK?&E_f#8$;d9v)>- z65ZRY1M$w|lIgh&%I|uvg-zDi2b$&YY#%+(a~+WqX}A?=_i0IX{O3>0l{BR-jPxn? zt5@$_lQXpW-4x1eh5c|A)m&_d27I*sVkh+cr?JMIwe6L7A`^fj>T8Hk%U$OghZ50!e zMech_&7(>a1=eu$k>`vpgC1$CneB)%UKR`>1%AM-wn%P;nG_?&o#Qr(lW2u(J4Ui{ zm&p&qX@nmobahwQK#Y`+AO8l#EnEPQVYFSfE>yDsB+dZzqi=wlU4w2mcC6uh_vFnY z<#((yT_@`Geo*F|4F-3c?R0JJ<8NF%U}5eql<`+P6p&F zhW`N4ZTlS{J8|jkq~!p?4mcB(?Kjh=*%;u>+fQO_WUW4&F+D?>bvhS3k@}ZU2kY9^ z!ZpGz+tNfFaTTD)?U(S#@W5A1U3Pd*9J_<)SwM#3%ENJHVK^e2nSr3-#fx8&Ho|9B zydGtc5V3=Fn&uOVpw&-FOWerkfSdUTG0cN&Upoi9Ps|GHggqnx_->M4(raFfh$evM z*ZSYe0^u0kC93FF7Pz(LVoC2W0xFu24*;zaK7!4{&C}&;3`^e(aE?LvZj13^|KL-= z!}yBj!5pX6SRs+1wUO7PrQ|=940Ap= z8u!DnD#$tNY=A3v{91A=`Ur4Js^>1uX*qPxn*kLIE+#zcfl$#A z*yoG;smPAzsast%qq)UAKRBD32~EqXbBKw`gD;pMG_Q0BkqTOv?0`rf3P_$LKu^E{ zP{?bpNc&svflUe_dNqZ06Dt`b1YA$5!Ue+2@Nfs1H4qUqjI1Z5RLEdd50!5ntW;bn zZNaUG1^F@_2`M_#-v}C+Mn)bo0>uJ*XgH_>JUQ~%BAEtJGwx7JZ4Lge$Y)oy^?h6( zpiq1pTeAEf8VLxdsrH7xRleDP_Y7%S6e23K8`5OQSHE|rDJ@uoQUf*eB_OKmMh67I zNx6#DqHS7-@}f@>!zpxd^C+X4^byf|{3*iI{C*U=C}%LGrBGeUp*B+SgpYO`0^ zKGnC`ZaG?e_*IDx|3(vMxTLpiAghK_)eJYpu7gAu_Bn{uq9T6r#m`S^1gtd*eMpr} zZTCyLV5hXp6z9>DjCnf*Od$;uAD_C)t<9cG+kmD$4IauFlp_a7BrT~2u{clQH9^{= z4|z}6UrdaN-HV1mVU-9nytlEhJ(pE~XAyLlKq5$`#t{i4)P__*7`Ex% zh}P>EK0t~ZYr(g^v$UmBZ| zNy*8=4lEY`MZ)sO0XEj^T6Us;z#C8m36u}|V}+Uj!0$#=rhaZ|+}aJ3S9tBMrU~M? zWkB5@1NK6K^tejgCVjxgPNuo>oyc)|CEm{+&}uBtdrOsCuFYdNt!9ZxVZ?uIyco0%zGy3hh zqcg9>MPX9LYVJ;VNC?fRRB~jhW@b;jf3wCPW3`-eFDkK$0jYs~d%bS@o?6|WH{_IW zP=nF6WOomCy?k_kl6Q@zALQVF3q6)xcMh~*#b->aGHbq#^R7v6SH$}!vMQvWifqR@ zJSD5$`${p@z$Q~~^Z?M){BBY(QXtIZKWxZ$2~m(!C=3>IYprJof>Lt4_Zo6A)eLuc znmF4)M5n5!C>eC2E*X-OQa%dAJW9xd#gFRk#dmD*tyX~=)%J(cqXc1dQt3&x`d2M3 ze4I5{+YoyMW=VU1spVPsn0DQi8|aXIK6>cQjnR)H55atZkO=6F6+cu9fLV?{hn->h z9WvDo9W7XLS3t<1-~oqsaplr7V8O{$Jpk6A`UPUv#l7VYs1^W)H@@3OO0!@Zzo#5^y@ISKy+LhHdM$=7fr%4Bb+0L);_BLM>)1b=l`6Olst`l%h$4 zP*&&3fa~n+Ai-_H>KJgrGe7K{#h9UK5LpV#&o9ftU>9G2JlhhWg02-!^U+B69FX=~ zwckokKOVHZ7Ljn4hSUuZ+{AG;#B4>+Tn5hgMpJh#zCG`IFN3hh z7jEVDqT|)k*}$IuazBtpPGg<@ydq*I3fc% z&kx=q?{rnr$Iy|}&$|~6!YEE1qVnQ0_rBIjx$vbm+cSpgot;6#hj)gU2YN-clU4u#2{Bv>?1E)2{9@BiTBL5e34eIf4S=5FAyvC~piMJ?r{ zgDug6v`tNl6^|KUFvg4E+n(Af|Mfr7t`K%YiufFXyfgqu&{$KVNXuB&qfcHT|Wl9qf*HX zh?{YH6OOIJYIc@f6*+Lu2I6z7Cm&^Cw*xGG;r9~SU&xOA>Yw-N} zLsZYW4pJm6miN(ty$MZ|1@;D*CVy<7PJy12wpMz7_d`oM#y4PhfUNooD4Ia9OB9gL zydIiTm3ss^Y-FYrYnqF~^qmbe);XL&(m44sp0*DOy;=$RRVPhjOD_D2K7=MN{kn7( zj0>v%VNdR=c@>!y{+i1FvJ&3c_erkgf?Z!Ks4<(oUn^8S0Q(0Z3REwPG%_p-l*-!L z{4v5MjV8UlpFi9^f&yp*NLs-%K!5*4=}3vf6Gc#cCXPj-nwVrRBE}>I!ooL=HfTFD z-iex-P4&i!FVl%(b6wm=?JXJoz}bh62DDxQH9SCx{z&iX1}YTd=mbq|_P58f$F0y5 z<+8HrrD-FVk?KBCEuG%nVgOr$P;i^#sN;S)-N}_34JrUo&I=Z`mFM7BE7YtOntkt2 z<*m~q6hu#-=rZzBRlOj34vd5;13%D+C!BckH$~eY*8xr{8n`dMq6Q0gLTFRZI4fWH zTWnniLTx5e)}`sk`4@EOK{y7WWNBXt=a=+X+-|uW@E7JmKv}-n~eYxpmy0a}luI-s6zAIj2qEBi?+BIbJig zM~K-_+G4eHjFY1%IlG*WQ|6v_yH;u4c=;WDE$SAMDbOiaip#%Rw4QG|)~fk@>F~9b zipGr|v_ht+TY*BTdB|K+rm4f6){m#i_~}%n@|+j~XJ=Y_ku+N;S=c2I`MN8sy(61Y z$zz!V;!x}6wsX+j`V)X!?yV($@a``Tc3beN>dC!U%eH?OTbEjD+;_ooQ88LQ^gE@u z)TTTucPt^m4d#5GS(DjVl`L`pslI^N2%y2JLaShQ0w~e|xT9`;WrJjSqVMe-1~qQv zLKP%C0Tgkg_HJ^MBx7)v8vIdy`WO{eB}pIlX9M!=a^wTRP2)1$Ny26uCPzhCn-OL+ zzPl-7Ki__OP#hvm!;DTZTS(D+{50k=Dp3RtZlLOD)6f7)r7E`wpbes)%m9%Gw(;uC zNygvgN&&NJgQ$r3Jclv6uI~00Z6FjW28xS=;^K$ygX%^~<-idWNdHMYL{iB9(0>L* zfggRUfEJkFZa~;Ky4AcJVySJf&4~hDHq0VG>OYD^DW+g*8X8dR3otzeZQ6gUx5-T) z$Pd;})pg)25NCH!*>?J5<`fik&9Xzo6b=HzBoGJ%I@w(PIoPwH&_WfWf!V=E`0s4p ztOV)+O3*->GhBEnnAM7%_5m$3)-8|VjHJqePe-pls3aI-1z(eNs+9KG_8Z6ii;==w zc@G179w{D(R}gk!khnfI93PLDzDG{=TMp1~z#YlNe)DENX4g71iy0muU(>;vYIITP z_Msj?F+74gc7y6q>2lr!9XtaQT&_CIaFch@>8OwZ1~VNI+Qn`9+!^-$ZJo$*JHr;DP3@*?A?ULPYek0uY!Uwi=%Y{9!(ncU%$Q=Fo z?`P)N1`=bRKL@IhPN4ELg8EdYlmAEjuBL3GbVH?20 z7oBwkdIDu2$n0>l+s@6c-kLV|4!Y$drUCQ#`8J^VUticT=(%uRoR6qWdGQ8gM|Yh_ zxyz0_z^V9c0A992bOLXH@ZmKr=jG+)_ue}3`wbBN;Ozy!T0LuLr;^SCnbuZ3aq}rq z31JE+UN%~nrU5cGK%lgMbc#jg!_{`x5J=)K?kT_e<3~NvJMLBgPcSiT9(FW#u3tVB zV~uH_uc^`1v$fR^^vOm(a^FmiTm>rxNNy^+y5i%avt3!fyIDRv$qd$=$9dTJ_!Q1G zBGX0jgm7V3>CVKbgl%6zz4g|(9N=u3q z&~Oa|Hspu5i96`d?ytJ(00z?3(%SNph_|;ePF%?RqP?Y+tJ9e@JWv+0?LR@e0^}s0 zhu44*b|aU@D|G?9_+h0qJTh|I-;nRC(1KgdN-3SB&w$&yfx5TQFn=2k2u%lgo|?D0 zA{q7wdEVFLPDqZqXCoIke!f%jGR!O0#e;!QHPZ_AtY-z#ua-It_@@;b7zGb5cJ}s8 zZttbQtD$#q)U-~HmDUZA6#xqWRB)?-PY!#6(XDCuruH3cb9fgZdxf=~G{SY3ivj{x ziDh0^R(Ak{PXaJ{Ay4?Yi)q6*yO7RumzkCrmi`A2QYhNqYyX?EH{D;HRfF8;1q|78 z3H^IgKy( zNZH;Tg+ya8UH7Ca&U!W7M&MU@XB1c@fHhkdN&uT&+4OQ`kbq=v%wW9HdASShmn#yR z$Cf6)y}j07V;>J`f!2weD&AwXk#OD#F!pu7Px|3GKD2JXV}<2jVHZnWqZB?s0ptEDx4lS?j=Fd7TJ0>cIzQtun--T_uXZ=*LHV9>EAICzdF#r zhNQyV^KO_dZTn5HM#7%tFwQ2gIBzwEEDCEF)6U=$YpisIBV!3462`b@3yF`U|Njlc8IL z85|@fe*4vdRvDpZ^wLzQN9n!rZ9sL@(ACW#%nWrdrH$<=^tTH8h_x{5{A=S}GJWnrw}q-#L4ntx} zSwkk$eFV2904Y&Dc=+U5DIn>CNSJfz{xd3Z9Cs$=mF2ahrJn}ViLszperD8Qv6Wrp zk^(PA+^lmDl0Ka085kCbY4uai(;xzY_Y|~l$KE>J;y_PhD;N!AmH@h*vo}RPRRN^g z`B;KF@I=-_m8@aCse!(q0*!TLp{_yt_nqfeN6S7nqrtEOFXcwa{b7-30ZT>$CW0M>zPHy=h|=h(NRfXGz1(&Iq8U%vCj$euDZaxc8t3+*mYuid(*-GY6gZA zZ*I1_gg3Ukvw$znTlPPNOL~6X`RkmPH6l~1h7bX%Ww$3Qq^ezYNSf)$9JF*+qs7|emU?FfnE*SMd{%BtUIp{DE z+ZrnRWSF=+OthZsFNR#|^z?Mtn~@H)NN;J$oz4(oiP-D~=yd{*zO3|Dd6t5U@_TJi zD8-6u-Zb)HIB{?SYd;840ZYo~h=!Y4AcnEPIoR1d3s>ShfpjX#GEnAeUZvV*x5Z@b zaUR=}c0kA{EGzJ201`OZ_~5kwQ)_rutDebDejVK8$Cl1U@R1{6{AQfQ^@7oP% z!s#gwLozcn&z(J+JXEzkiPf{mV2GzLGn#0?NptN&*7EpfCRU;uumr f_aELXvO+wt$rD`O5jBT{PJ+uQ-_5=A;K~00H$!n| literal 19894 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 BUXB0& diff --git a/src/napari_matplotlib/tests/test_histogram.py b/src/napari_matplotlib/tests/test_histogram.py index 399db3d..435973b 100644 --- a/src/napari_matplotlib/tests/test_histogram.py +++ b/src/napari_matplotlib/tests/test_histogram.py @@ -17,9 +17,7 @@ def test_histogram_2D_bins(make_napari_viewer, astronaut_data): viewer.add_image(astronaut_data[0], **astronaut_data[1]) widget = HistogramWidget(viewer) viewer.window.add_dock_widget(widget) - widget.bins_start = 0 - widget.bins_stop = 350 - widget.bins_num = 35 + widget.num_bins_widget.setValue(25) fig = widget.figure # Need to return a copy, as original figure is too eagerley garbage # collected by the widget From 8fe7c7f0b75da3a8018ebead7ddfc4823c338b35 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 12 Jul 2024 16:24:48 +0200 Subject: [PATCH 22/37] Update changelog --- docs/changelog.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 4c2509f..60dd72b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,12 @@ Changelog ========= +2.1.0 +----- +New features +~~~~~~~~~~~~ +- Added a GUI element to manually set the number of bins in the histogram widgets. + 2.0.3 ----- Bug fixes @@ -48,7 +54,6 @@ Other changes - The ``HistogramWidget`` now has two vertical lines showing the contrast limits used to render the selected layer in the main napari window. - Added an example gallery for the ``FeaturesHistogramWidget``. -- Add widgets for setting bin parameters for ``HistogramWidget``. 1.2.0 ----- From 597e1c5aee7c92d22217887092a4e67303af62f3 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 12 Jul 2024 16:55:24 +0200 Subject: [PATCH 23/37] Update test image --- .../scatter/baseline/test_scatter_2D.png | Bin 18394 -> 18788 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/napari_matplotlib/tests/scatter/baseline/test_scatter_2D.png b/src/napari_matplotlib/tests/scatter/baseline/test_scatter_2D.png index a11bda5f281b731712a2d1c6076f72569e0ff5e0..0685b192b01b00ab02c5bceb4d4c966a8264cb4a 100644 GIT binary patch literal 18788 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 Date: Mon, 15 Jul 2024 20:06:37 +0100 Subject: [PATCH 24/37] Revert "Update test image" This reverts commit 597e1c5aee7c92d22217887092a4e67303af62f3. --- .../scatter/baseline/test_scatter_2D.png | Bin 18788 -> 18394 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/napari_matplotlib/tests/scatter/baseline/test_scatter_2D.png b/src/napari_matplotlib/tests/scatter/baseline/test_scatter_2D.png index 0685b192b01b00ab02c5bceb4d4c966a8264cb4a..a11bda5f281b731712a2d1c6076f72569e0ff5e0 100644 GIT binary patch literal 18394 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 Date: Mon, 22 Jul 2024 17:45:02 +0000 Subject: [PATCH 25/37] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.10.1 → v1.11.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.1...v1.11.0) - [github.com/astral-sh/ruff-pre-commit: v0.5.1 → v0.5.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.1...v0.5.4) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ebae2d3..54ee202 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,14 +17,14 @@ repos: - id: napari-plugin-checks - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.1 + rev: v1.11.0 hooks: - id: mypy additional_dependencies: [numpy, matplotlib] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.5.1' + rev: 'v0.5.4' hooks: - id: ruff From 2e8330f509a93f30a6a1f8ccc6cd6d7627e04122 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:48:13 +0000 Subject: [PATCH 26/37] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.4 → v0.5.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.4...v0.5.5) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 54ee202..7ede235 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.5.4' + rev: 'v0.5.5' hooks: - id: ruff From 3db8cae7298debeea16577065739047feeab137a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 17:56:44 +0000 Subject: [PATCH 27/37] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.11.0 → v1.11.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.0...v1.11.1) - [github.com/astral-sh/ruff-pre-commit: v0.5.5 → v0.5.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.5...v0.5.6) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7ede235..14ccabc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,14 +17,14 @@ repos: - id: napari-plugin-checks - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.0 + rev: v1.11.1 hooks: - id: mypy additional_dependencies: [numpy, matplotlib] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.5.5' + rev: 'v0.5.6' hooks: - id: ruff From 6e0348dafef343dbaf9272c1231cbfed33b4cc7c Mon Sep 17 00:00:00 2001 From: Kabilar Gunalan Date: Thu, 8 Aug 2024 16:16:28 -0500 Subject: [PATCH 28/37] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e4551d2..fb7aa63 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A plugin to create Matplotlib plots from napari layers ## Introduction `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. -Documentaiton can be found at https://napari-matplotlib.github.io/ +Documentation can be found at https://napari-matplotlib.github.io/ ## Contributing From 0efe4432143760800872f224e45b9a295e7619cb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 17:42:28 +0000 Subject: [PATCH 29/37] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black-pre-commit-mirror: 24.4.2 → 24.8.0](https://github.com/psf/black-pre-commit-mirror/compare/24.4.2...24.8.0) - [github.com/astral-sh/ruff-pre-commit: v0.5.6 → v0.6.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.6...v0.6.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14ccabc..cbf9cb8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.2 + rev: 24.8.0 hooks: - id: black @@ -24,7 +24,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.5.6' + rev: 'v0.6.1' hooks: - id: ruff From 3dbfc475bd981cfe11a48c21eb9af81c1f44ca58 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 17:48:50 +0000 Subject: [PATCH 30/37] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.11.1 → v1.11.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.1...v1.11.2) - [github.com/astral-sh/ruff-pre-commit: v0.6.1 → v0.6.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.1...v0.6.2) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cbf9cb8..26d290e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,14 +17,14 @@ repos: - id: napari-plugin-checks - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.1 + rev: v1.11.2 hooks: - id: mypy additional_dependencies: [numpy, matplotlib] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.6.1' + rev: 'v0.6.2' hooks: - id: ruff From 0acbb986caa2637881369787c403b70e8390d8bf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 17:43:14 +0000 Subject: [PATCH 31/37] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.2 → v0.6.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.2...v0.6.3) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 26d290e..bd4cb83 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.6.2' + rev: 'v0.6.3' hooks: - id: ruff From 0878bc1af0d261ea01ae82c6eb18b3428c3aae6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:06:11 +0000 Subject: [PATCH 32/37] Bump actions/download-artifact from 3 to 4.1.7 in /.github/workflows Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.1.7. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4.1.7) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 39c1ab5..e1a8621 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -50,7 +50,7 @@ jobs: if: contains(github.ref, 'tags') steps: - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4.1.7 with: name: docs From 183f4e2e9e5bdd21ba960c351de44bce15dd8355 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:45:46 +0000 Subject: [PATCH 33/37] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.3 → v0.6.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.3...v0.6.4) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bd4cb83..9b8c446 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.6.3' + rev: 'v0.6.4' hooks: - id: ruff From d1b75b1e2a4355f7a338f60089500e188de8226a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:43:13 +0000 Subject: [PATCH 34/37] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.4 → v0.6.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.4...v0.6.5) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9b8c446..8fb0414 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.6.4' + rev: 'v0.6.5' hooks: - id: ruff From bbba362e8cee5eaa96b0c8afa117df648708cef8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:43:16 +0000 Subject: [PATCH 35/37] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.5 → v0.6.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.5...v0.6.7) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8fb0414..331601a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.6.5' + rev: 'v0.6.7' hooks: - id: ruff From d21e7c8a2390040722651807ed9988184952ae58 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:47:20 +0000 Subject: [PATCH 36/37] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.7 → v0.6.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.7...v0.6.8) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 331601a..e807909 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.6.7' + rev: 'v0.6.8' hooks: - id: ruff From 7e860f541d3398adaa567f3e56c665e2c58d5bfc Mon Sep 17 00:00:00 2001 From: David Stansby Date: Tue, 1 Oct 2024 12:35:54 +0100 Subject: [PATCH 37/37] Support napari>=0.5 (#274) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove as_dict kwarg * Pin to napari >= 0.5 * Fix scatter test? * Update test image * Fix brain data * Fix another as_dict arg * Fix FutureWarning * Fix scatter test's OverflowError And revert to original baseline images. * Try updating mypy's python version. * Revert shape → newshape and suppress deprecation warning. --------- Co-authored-by: Sam Cunliffe Co-authored-by: Sam Cunliffe --- pyproject.toml | 20 ++++++++++++------- setup.cfg | 2 +- src/napari_matplotlib/base.py | 6 +++--- .../tests/scatter/test_scatter.py | 6 ++++-- src/napari_matplotlib/tests/test_theme.py | 2 +- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 05f7df6..f76831a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,10 +13,18 @@ filterwarnings = [ "ignore:distutils Version classes are deprecated:DeprecationWarning", "ignore:`np.bool8` is a deprecated alias for `np.bool_`:DeprecationWarning", # Coming from pydantic via napari - "ignore:Pickle, copy, and deepcopy support will be removed from itertools in Python 3.14.:DeprecationWarning" + "ignore:Pickle, copy, and deepcopy support will be removed from itertools in Python 3.14.:DeprecationWarning", + # Until we stop supporting older numpy versions (<2.1) + "ignore:(?s).*`newshape` keyword argument is deprecated.*$:DeprecationWarning", ] qt_api = "pyqt6" -addopts = ["--mpl", "--mpl-baseline-relative", "--strict-config", "--strict-markers", "-ra"] +addopts = [ + "--mpl", + "--mpl-baseline-relative", + "--strict-config", + "--strict-markers", + "-ra", +] minversion = "7" testpaths = ["src/napari_matplotlib/tests"] log_cli_level = "INFO" @@ -54,17 +62,15 @@ ignore = [ convention = "numpy" [tool.mypy] -python_version = "3.10" +python_version = "3.12" # Block below are checks that form part of mypy 'strict' mode strict = true disallow_subclassing_any = false # TODO: fix -warn_return_any = false # TODO: fix +warn_return_any = false # TODO: fix ignore_missing_imports = true enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] [[tool.mypy.overrides]] -module = [ - "napari_matplotlib/tests/*", -] +module = ["napari_matplotlib/tests/*"] disallow_untyped_defs = false diff --git a/setup.cfg b/setup.cfg index 073478a..a3709e6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ project_urls = packages = find: install_requires = matplotlib - napari<0.5 + napari>=0.5 numpy>=1.23 tinycss2 python_requires = >=3.10 diff --git a/src/napari_matplotlib/base.py b/src/napari_matplotlib/base.py index 720333e..ca69a54 100644 --- a/src/napari_matplotlib/base.py +++ b/src/napari_matplotlib/base.py @@ -42,7 +42,7 @@ def __init__( super().__init__(parent=parent) self.viewer = napari_viewer self.napari_theme_style_sheet = style_sheet_from_theme( - get_theme(napari_viewer.theme, as_dict=False) + get_theme(napari_viewer.theme) ) # Sets figure.* style @@ -84,7 +84,7 @@ def _on_napari_theme_changed(self, event: Event) -> None: Event that triggered the callback. """ self.napari_theme_style_sheet = style_sheet_from_theme( - get_theme(event.value, as_dict=False) + get_theme(event.value) ) self._replace_toolbar_icons() @@ -97,7 +97,7 @@ def _napari_theme_has_light_bg(self) -> bool: bool True if theme's background colour has hsl lighter than 50%, False if darker. """ - theme = napari.utils.theme.get_theme(self.viewer.theme, as_dict=False) + theme = napari.utils.theme.get_theme(self.viewer.theme) _, _, bg_lightness = theme.background.as_hsl_tuple() return bg_lightness > 0.5 diff --git a/src/napari_matplotlib/tests/scatter/test_scatter.py b/src/napari_matplotlib/tests/scatter/test_scatter.py index a225863..0c60660 100644 --- a/src/napari_matplotlib/tests/scatter/test_scatter.py +++ b/src/napari_matplotlib/tests/scatter/test_scatter.py @@ -15,7 +15,9 @@ def test_scatter_2D(make_napari_viewer, astronaut_data): viewer.add_image(astronaut_data[0], **astronaut_data[1], name="astronaut") viewer.add_image( - astronaut_data[0] * -1, **astronaut_data[1], name="astronaut_reversed" + astronaut_data[0] * -1.0, + **astronaut_data[1], + name="astronaut_reversed", ) # De-select existing selection viewer.layers.selection.clear() @@ -36,7 +38,7 @@ def test_scatter_3D(make_napari_viewer, brain_data): viewer.add_image(brain_data[0], **brain_data[1], name="brain") viewer.add_image( - brain_data[0] * -1, **brain_data[1], name="brain_reversed" + brain_data[0] * -1.0, **brain_data[1], name="brain_reversed" ) # De-select existing selection viewer.layers.selection.clear() diff --git a/src/napari_matplotlib/tests/test_theme.py b/src/napari_matplotlib/tests/test_theme.py index 2310f32..5fedc43 100644 --- a/src/napari_matplotlib/tests/test_theme.py +++ b/src/napari_matplotlib/tests/test_theme.py @@ -29,7 +29,7 @@ def _mock_up_theme() -> None: Based on: https://napari.org/stable/gallery/new_theme.html """ - blue_theme = napari.utils.theme.get_theme("dark", False) + blue_theme = napari.utils.theme.get_theme("dark") blue_theme.label = "blue" blue_theme.background = "#4169e1" # my favourite shade of blue napari.utils.theme.register_theme( pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy