From a7b09d4ce297cad9dd6fa424403c8398ec20bc74 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Thu, 11 Jan 2024 10:55:41 +0000 Subject: [PATCH 001/101] 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 002/101] 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 003/101] 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 004/101] 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 005/101] 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 006/101] 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 007/101] 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 008/101] 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 009/101] 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 010/101] 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 011/101] 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 012/101] 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 013/101] 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 014/101] 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 015/101] 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 016/101] 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 017/101] 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 018/101] 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 109418e4557e3b84cfe74602c6329c18047fb19f Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 19:12:59 +0000 Subject: [PATCH 019/101] Use 101 bin edges for histograms of float data --- src/napari_matplotlib/histogram.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 073620d..8269d98 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -26,8 +26,9 @@ 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 - return np.linspace(np.min(data), np.max(data), 100) + # For other data types, just have 100 evenly spaced bins + # (and 101 bin edges) + return np.linspace(np.min(data), np.max(data), 101) class HistogramWidget(SingleAxesWidget): From 101b4e5fe0e473f7440aa32879011d71036c5379 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Mon, 15 Jan 2024 19:16:08 +0000 Subject: [PATCH 020/101] update baseline images for histogram tests with float data --- .../test_feature_histogram_points.png | Bin 17145 -> 17200 bytes .../test_feature_histogram_vectors.png | Bin 17259 -> 17725 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/napari_matplotlib/tests/baseline/test_feature_histogram_points.png b/src/napari_matplotlib/tests/baseline/test_feature_histogram_points.png index be8770ee9229ae198086224cd76da3ce6c4df007..88a28f79c8b07efca0319bd565bc306ba4a35c37 100644 GIT binary patch literal 17200 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 literal 17145 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 Xw9+@;gCb23@CP$6yHtMh`u+a_hA-RY diff --git a/src/napari_matplotlib/tests/baseline/test_feature_histogram_vectors.png b/src/napari_matplotlib/tests/baseline/test_feature_histogram_vectors.png index 119c28e1269af57f8d8cff5c5da2e84a1ac306da..857d93449651193b77014826950c8a3d61e608d4 100644 GIT binary patch literal 17725 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 R5yqw3>iV}!ZrMEj{{T~dlZgNT literal 17259 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 H4p09FU@Qgk From ae412cb24421c9b18e9a1b92317844bdfa191290 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:29:12 +0000 Subject: [PATCH 021/101] [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.1.11 → v0.1.14](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.11...v0.1.14) --- .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 8df635a..9f84305 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.1.11' + rev: 'v0.1.14' hooks: - id: ruff From 34c9e2c3226370717c2f86703a21154dd56d8607 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Tue, 23 Jan 2024 18:33:34 +0000 Subject: [PATCH 022/101] Fix HistogramWidget for multiscale data --- src/napari_matplotlib/histogram.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 073620d..ffbd1ff 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -4,6 +4,8 @@ import numpy as np import numpy.typing as npt from matplotlib.container import BarContainer +from napari.layers import Image +from napari.layers._multiscale_data import MultiScaleData from qtpy.QtWidgets import ( QComboBox, QLabel, @@ -67,14 +69,18 @@ def draw(self) -> None: """ Clear the axes and histogram the currently selected layer/slice. """ - layer = self.layers[0] + layer: Image = self.layers[0] + data = layer.data - if layer.data.ndim - layer.rgb == 3: + if isinstance(layer.data, MultiScaleData): + data = data[layer.data_level] + + if layer.ndim - layer.rgb == 3: # 3D data, can be single channel or RGB - data = layer.data[self.current_z] + # Slice in z dimension + data = data[self.current_z] 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) From 31c8fac9bacb87c34a6dffa46781ac7849be1c70 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Tue, 23 Jan 2024 18:39:17 +0000 Subject: [PATCH 023/101] Fix pandas deprecation warning --- pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 22ff307..ba9f9e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,14 +6,15 @@ build-backend = "setuptools.build_meta" write_to = "src/napari_matplotlib/_version.py" [tool.pytest.ini_options] -qt_api = "pyqt6" -addopts = "--mpl --mpl-baseline-relative" filterwarnings = [ "error", + "ignore:(?s).*Pyarrow will become a required dependency of pandas", # Coming from vispy "ignore:distutils Version classes are deprecated:DeprecationWarning", "ignore:`np.bool8` is a deprecated alias for `np.bool_`:DeprecationWarning", ] +qt_api = "pyqt6" +addopts = "--mpl --mpl-baseline-relative" [tool.black] line-length = 79 From c3c4dbd2774da51fbb9a8a072363201129bce3df Mon Sep 17 00:00:00 2001 From: David Stansby Date: Tue, 23 Jan 2024 18:47:48 +0000 Subject: [PATCH 024/101] Add 2.0.1 changelog --- docs/changelog.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 6395e9c..255982a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,5 +1,13 @@ Changelog ========= +2.0.1 +----- +Bug fixes +~~~~~~~~~ +- Fixed using the ``HistogramWidget`` with layers containing multiscale data. +- Make sure ``HistogramWidget`` uses 100 bins (not 99) when floating point data is + selected. + 2.0.0 ----- Changes to custom theming From 266f4849136a4b11be6d4b27dffb886058e3cf2e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:32:20 +0000 Subject: [PATCH 025/101] [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: 23.12.1 → 24.1.1](https://github.com/psf/black/compare/23.12.1...24.1.1) - [github.com/astral-sh/ruff-pre-commit: v0.1.14 → v0.2.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.14...v0.2.0) --- .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 9f84305..5bff103 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 - rev: 23.12.1 + rev: 24.1.1 hooks: - id: black @@ -24,7 +24,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.1.14' + rev: 'v0.2.0' hooks: - id: ruff From 0efd2b2c03e3dcc1cd455489af5a1fc813527935 Mon Sep 17 00:00:00 2001 From: Sam Cunliffe Date: Tue, 6 Feb 2024 19:31:50 +0000 Subject: [PATCH 026/101] Expecting two warnings so nested with pytest.warns. --- src/napari_matplotlib/tests/test_util.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/napari_matplotlib/tests/test_util.py b/src/napari_matplotlib/tests/test_util.py index a8792d4..918e763 100644 --- a/src/napari_matplotlib/tests/test_util.py +++ b/src/napari_matplotlib/tests/test_util.py @@ -69,7 +69,10 @@ def test_fallback_if_missing_dimensions(mocker): test_css = " Flobble { background-color: rgb(0, 97, 163); } " mocker.patch("napari.qt.get_current_stylesheet").return_value = test_css with pytest.warns(RuntimeWarning, match="Unable to find DimensionToken"): - assert from_napari_css_get_size_of("Flobble", (1, 2)) == QSize(1, 2) + with pytest.warns(RuntimeWarning, match="Unable to find Flobble"): + assert from_napari_css_get_size_of("Flobble", (1, 2)) == QSize( + 1, 2 + ) def test_fallback_if_prelude_not_in_css(): From cd429ee947b9e8dc949851bab0ef106d157b0531 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 09:45:06 +0000 Subject: [PATCH 027/101] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/histogram.py | 1 + examples/scatter.py | 1 + examples/slice.py | 1 + 3 files changed, 3 insertions(+) diff --git a/examples/histogram.py b/examples/histogram.py index ccda491..b9ceb37 100644 --- a/examples/histogram.py +++ b/examples/histogram.py @@ -2,6 +2,7 @@ Histograms ========== """ + import napari viewer = napari.Viewer() diff --git a/examples/scatter.py b/examples/scatter.py index cd81240..00e01ec 100644 --- a/examples/scatter.py +++ b/examples/scatter.py @@ -2,6 +2,7 @@ Scatter plots ============= """ + import napari viewer = napari.Viewer() diff --git a/examples/slice.py b/examples/slice.py index 3e43443..242a16c 100644 --- a/examples/slice.py +++ b/examples/slice.py @@ -2,6 +2,7 @@ 1D slices ========= """ + import napari viewer = napari.Viewer() From 07217c621302d644a8796e878cf70bfdde2f2164 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 17:33:44 +0000 Subject: [PATCH 028/101] [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.2.0 → v0.2.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.2.0...v0.2.1) --- .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 5bff103..f97ded2 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.2.0' + rev: 'v0.2.1' hooks: - id: ruff From 6261f4c36fb531146cb270f3ad2bf9fc5d62ac1c Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Wed, 14 Feb 2024 10:54:40 +0000 Subject: [PATCH 029/101] 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 46058eea210c3b7c532d0e857f05592e15568de5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 17:43:22 +0000 Subject: [PATCH 030/101] [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: 24.1.1 → 24.2.0](https://github.com/psf/black/compare/24.1.1...24.2.0) - [github.com/astral-sh/ruff-pre-commit: v0.2.1 → v0.2.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.2.1...v0.2.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 f97ded2..e57a9c8 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 - rev: 24.1.1 + rev: 24.2.0 hooks: - id: black @@ -24,7 +24,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.2.1' + rev: 'v0.2.2' hooks: - id: ruff From 517b2139ccfe59a4cc486467692a0e62bb9839aa Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 3 May 2024 15:21:40 +0100 Subject: [PATCH 031/101] Update pre-commit --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e57a9c8..40048ea 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,13 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-docstring-first - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 24.2.0 + rev: 24.4.2 hooks: - id: black @@ -17,14 +17,14 @@ repos: - id: napari-plugin-checks - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 + rev: v1.10.0 hooks: - id: mypy additional_dependencies: [numpy, matplotlib] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.2.2' + rev: 'v0.4.2' hooks: - id: ruff From 9949427de3b7212931e2601baabf2040c7cbfe22 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 3 May 2024 15:36:11 +0100 Subject: [PATCH 032/101] Bump codecov action --- .github/workflows/test_and_deploy.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 8665e1d..9b24e7f 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -58,7 +58,7 @@ jobs: if: ${{ always() }} - name: Coverage - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 # Don't run coverage on merge queue CI to avoid duplicating reports # to codecov. See https://github.com/matplotlib/napari-matplotlib/issues/155 if: github.event_name != 'merge_group' @@ -66,6 +66,8 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true + + deploy: # this will run when you have tagged a commit, starting with "v*" # and requires that you have put your twine API key in your From ea21e54a0c30ceafb3b7c1bdbf7e9ab77f8629c7 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 3 May 2024 15:47:12 +0100 Subject: [PATCH 033/101] Suppress doc build warning --- docs/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 2517a59..5be3c95 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -33,6 +33,7 @@ "sphinx_automodapi.automodapi", "sphinx_automodapi.smart_resolver", "sphinx.ext.intersphinx", + "qtgallery", ] sphinx_gallery_conf = { @@ -40,6 +41,7 @@ "image_scrapers": (qtgallery.qtscraper,), "reset_modules": (qtgallery.reset_qapp,), } +suppress_warnings = ["config.cache"] qtgallery_conf = { "xvfb_size": (640, 480), From 2645f43d139202da084aba3cecaa5b19f762093b Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 3 May 2024 15:54:05 +0100 Subject: [PATCH 034/101] Remove napari hub preview --- .github/workflows/napari_hub_preview.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .github/workflows/napari_hub_preview.yml diff --git a/.github/workflows/napari_hub_preview.yml b/.github/workflows/napari_hub_preview.yml deleted file mode 100644 index c204ac4..0000000 --- a/.github/workflows/napari_hub_preview.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: napari hub Preview Page # we use this name to find your preview page artifact, so don't change it! -# For more info on this action, see https://github.com/chanzuckerberg/napari-hub-preview-action/blob/main/action.yml - -on: - pull_request: - types: [ labeled ] - -jobs: - preview-page: - if: ${{ github.event.label.name == 'napari hub preview' }} - name: Preview Page Deploy - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - - name: napari hub Preview Page Builder - uses: chanzuckerberg/napari-hub-preview-action@v0.1 - with: - hub-ref: main From e5b9bb2b5bf5b00aaf0fcbf5598a235b08fd9c83 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 3 May 2024 15:54:42 +0100 Subject: [PATCH 035/101] Update headless gui version --- .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 5b721d9..39c1ab5 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -32,7 +32,7 @@ jobs: sudo apt install graphviz --yes - name: Build Docs - uses: aganders3/headless-gui@v1 + uses: aganders3/headless-gui@v2 with: run: make html working-directory: ./docs From 8cc74cab748cc0da54e6c08ea52d8d0a119fee79 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 3 May 2024 16:01:03 +0100 Subject: [PATCH 036/101] Try stable release of napari for doc build --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 41e4e34..25aa4cf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,7 +47,7 @@ napari.manifest = [options.extras_require] docs = - napari[all]==0.4.19rc3 + napari[all]==0.4.19 numpydoc pydantic<2 pydata-sphinx-theme From 0be3f7b70c60fd8cd44220ab4f1c6bfe24722770 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 3 May 2024 16:10:26 +0100 Subject: [PATCH 037/101] Remoe doc napari pin --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 25aa4cf..cccec1c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,7 +47,7 @@ napari.manifest = [options.extras_require] docs = - napari[all]==0.4.19 + napari[all] numpydoc pydantic<2 pydata-sphinx-theme From 797e4605e99e2773f9ebcbe5ddd0f687e316fb6e Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 24 May 2024 14:00:28 +0100 Subject: [PATCH 038/101] Try custom scrapers --- docs/conf.py | 57 +++++++++++++++++++++++++++++++++++++++++++--------- setup.cfg | 1 - 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5be3c95..3c6ce6f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ # import os # import sys # sys.path.insert(0, os.path.abspath('.')) -import qtgallery +from sphinx_gallery import scrapers # -- Project information ----------------------------------------------------- @@ -33,22 +33,59 @@ "sphinx_automodapi.automodapi", "sphinx_automodapi.smart_resolver", "sphinx.ext.intersphinx", - "qtgallery", ] + +def reset_napari(gallery_conf, fname): + from napari.settings import get_settings + from qtpy.QtWidgets import QApplication + + settings = get_settings() + settings.appearance.theme = "dark" + + # Disabling `QApplication.exec_` means example scripts can call `exec_` + # (scripts work when run normally) without blocking example execution by + # sphinx-gallery. (from qtgallery) + QApplication.exec_ = lambda _: None + + +def napari_scraper(block, block_vars, gallery_conf): + """Basic napari window scraper. + + Looks for any QtMainWindow instances and takes a screenshot of them. + + `app.processEvents()` allows Qt events to propagateo and prevents hanging. + """ + import napari + + imgpath_iter = block_vars["image_path_iterator"] + + if app := napari.qt.get_app(): + app.processEvents() + else: + return "" + + img_paths = [] + for win, img_path in zip( + reversed(napari._qt.qt_main_window._QtMainWindow._instances), + imgpath_iter, + ): + img_paths.append(img_path) + win._window.screenshot(img_path, canvas_only=False) + + napari.Viewer.close_all() + app.processEvents() + + return scrapers.figure_rst(img_paths, gallery_conf["src_dir"]) + + sphinx_gallery_conf = { "filename_pattern": ".", - "image_scrapers": (qtgallery.qtscraper,), - "reset_modules": (qtgallery.reset_qapp,), + "image_scrapers": (napari_scraper,), + "reset_modules": (reset_napari,), } suppress_warnings = ["config.cache"] -qtgallery_conf = { - "xvfb_size": (640, 480), - "xvfb_color_depth": 24, - "xfvb_use_xauth": False, - "xfvb_extra_args": [], -} numpydoc_show_class_members = False automodapi_inheritance_diagram = True diff --git a/setup.cfg b/setup.cfg index cccec1c..76fc8bf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,7 +51,6 @@ docs = numpydoc pydantic<2 pydata-sphinx-theme - qtgallery sphinx sphinx-automodapi sphinx-gallery From 312d50c03bd9e4df775c6baa17d728affe37658b Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 24 May 2024 14:03:57 +0100 Subject: [PATCH 039/101] Add some type ignores --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 3c6ce6f..2818f02 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,7 +36,7 @@ ] -def reset_napari(gallery_conf, fname): +def reset_napari(gallery_conf, fname): # type: ignore[no-untyped-def] from napari.settings import get_settings from qtpy.QtWidgets import QApplication @@ -49,7 +49,7 @@ def reset_napari(gallery_conf, fname): QApplication.exec_ = lambda _: None -def napari_scraper(block, block_vars, gallery_conf): +def napari_scraper(block, block_vars, gallery_conf): # type: ignore[no-untyped-def] """Basic napari window scraper. Looks for any QtMainWindow instances and takes a screenshot of them. From 5c80464405f4c5205628805911c33ef9bf0e1226 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 3 May 2024 15:27:26 +0100 Subject: [PATCH 040/101] Adhere to SPEC0 --- .github/workflows/test_and_deploy.yml | 2 +- docs/changelog.rst | 9 +++++++++ pyproject.toml | 4 ++-- setup.cfg | 4 ++-- tox.ini | 4 ++-- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 9b24e7f..94cf1e9 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false matrix: platform: [ubuntu-latest, macos-latest, windows-latest] - python-version: ['3.9', '3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v3 diff --git a/docs/changelog.rst b/docs/changelog.rst index 255982a..8e3e859 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,5 +1,14 @@ Changelog ========= +2.0.2 +----- +Dependencies +~~~~~~~~~~~~ +napari-matplotlib now adheres to `SPEC 0`_, and has: +- Dropped support for Python 3.9 +- Added support for Python 3.12 +- Added a minimum required numpy verison of 1.23 + 2.0.1 ----- Bug fixes diff --git a/pyproject.toml b/pyproject.toml index ba9f9e6..d569113 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ profile = "black" line_length = 79 [tool.ruff] -target-version = "py39" +target-version = "py310" select = ["I", "UP", "F", "E", "W", "D"] ignore = [ "D100", # Missing docstring in public module @@ -46,7 +46,7 @@ fix = true convention = "numpy" [tool.mypy] -python_version = "3.9" +python_version = "3.10" # Block below are checks that form part of mypy 'strict' mode strict = true disallow_subclassing_any = false # TODO: fix diff --git a/setup.cfg b/setup.cfg index 76fc8bf..2b74504 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,9 +29,9 @@ packages = find: install_requires = matplotlib napari - numpy + numpy>=1.23 tinycss2 -python_requires = >=3.9 +python_requires = >=3.10 include_package_data = True package_dir = =src diff --git a/tox.ini b/tox.ini index 4ec0c70..f4aed6a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,12 @@ [tox] -envlist = py{39,310,311} +envlist = py{310,311,312} isolated_build = true [gh-actions] python = - 3.9: py39 3.10: py310 3.11: py311 + 3.12: py312 [testenv] extras = testing From 891f4aa263e1d08dccd22e27f7b00941cf076268 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 24 May 2024 14:17:39 +0100 Subject: [PATCH 041/101] Ignore pydantic warning --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index d569113..bbec409 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,8 @@ filterwarnings = [ # Coming from vispy "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" ] qt_api = "pyqt6" addopts = "--mpl --mpl-baseline-relative" From 52bb0a902bc4c427f159a7ab4e4f2b969d33169b Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 24 May 2024 14:40:06 +0100 Subject: [PATCH 042/101] pre-commit fixes --- src/napari_matplotlib/base.py | 7 +++---- src/napari_matplotlib/histogram.py | 12 ++++++------ src/napari_matplotlib/scatter.py | 8 ++++---- src/napari_matplotlib/slice.py | 4 ++-- src/napari_matplotlib/util.py | 9 +++------ 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/napari_matplotlib/base.py b/src/napari_matplotlib/base.py index fb9e485..c455335 100644 --- a/src/napari_matplotlib/base.py +++ b/src/napari_matplotlib/base.py @@ -1,6 +1,5 @@ import os from pathlib import Path -from typing import Optional import matplotlib.style as mplstyle import napari @@ -38,7 +37,7 @@ class BaseNapariMPLWidget(QWidget): def __init__( self, napari_viewer: napari.Viewer, - parent: Optional[QWidget] = None, + parent: QWidget | None = None, ): super().__init__(parent=parent) self.viewer = napari_viewer @@ -173,7 +172,7 @@ class NapariMPLWidget(BaseNapariMPLWidget): def __init__( self, napari_viewer: napari.viewer.Viewer, - parent: Optional[QWidget] = None, + parent: QWidget | None = None, ): super().__init__(napari_viewer=napari_viewer, parent=parent) self._setup_callbacks() @@ -282,7 +281,7 @@ class SingleAxesWidget(NapariMPLWidget): def __init__( self, napari_viewer: napari.viewer.Viewer, - parent: Optional[QWidget] = None, + parent: QWidget | None = None, ): super().__init__(napari_viewer=napari_viewer, parent=parent) self.add_single_axes() diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 4076528..a0b6e09 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, cast import napari import numpy as np @@ -44,7 +44,7 @@ class HistogramWidget(SingleAxesWidget): def __init__( self, napari_viewer: napari.viewer.Viewer, - parent: Optional[QWidget] = None, + parent: QWidget | None = None, ): super().__init__(napari_viewer, parent=parent) self._update_layers(None) @@ -121,7 +121,7 @@ class FeaturesHistogramWidget(SingleAxesWidget): def __init__( self, napari_viewer: napari.viewer.Viewer, - parent: Optional[QWidget] = None, + parent: QWidget | None = None, ): super().__init__(napari_viewer, parent=parent) @@ -137,12 +137,12 @@ def __init__( self._update_layers(None) @property - def x_axis_key(self) -> Optional[str]: + def x_axis_key(self) -> str | None: """Key to access x axis data from the FeaturesTable""" return self._x_axis_key @x_axis_key.setter - def x_axis_key(self, key: Optional[str]) -> None: + def x_axis_key(self, key: str | None) -> None: self._x_axis_key = key self._draw() @@ -166,7 +166,7 @@ def _get_valid_axis_keys(self) -> list[str]: else: return self.layers[0].features.keys() - def _get_data(self) -> tuple[Optional[npt.NDArray[Any]], str]: + def _get_data(self) -> tuple[npt.NDArray[Any] | None, str]: """Get the plot data. Returns diff --git a/src/napari_matplotlib/scatter.py b/src/napari_matplotlib/scatter.py index 67d6896..98ebe92 100644 --- a/src/napari_matplotlib/scatter.py +++ b/src/napari_matplotlib/scatter.py @@ -1,4 +1,4 @@ -from typing import Any, Optional, Union +from typing import Any import napari import numpy.typing as npt @@ -100,7 +100,7 @@ class FeaturesScatterWidget(ScatterBaseWidget): def __init__( self, napari_viewer: napari.viewer.Viewer, - parent: Optional[QWidget] = None, + parent: QWidget | None = None, ): super().__init__(napari_viewer, parent=parent) @@ -118,7 +118,7 @@ def __init__( self._update_layers(None) @property - def x_axis_key(self) -> Union[str, None]: + def x_axis_key(self) -> str | None: """ Key for the x-axis data. """ @@ -133,7 +133,7 @@ def x_axis_key(self, key: str) -> None: self._draw() @property - def y_axis_key(self) -> Union[str, None]: + def y_axis_key(self) -> str | None: """ Key for the y-axis data. """ diff --git a/src/napari_matplotlib/slice.py b/src/napari_matplotlib/slice.py index 9459fa9..1924bf2 100644 --- a/src/napari_matplotlib/slice.py +++ b/src/napari_matplotlib/slice.py @@ -1,4 +1,4 @@ -from typing import Any, Optional +from typing import Any import matplotlib.ticker as mticker import napari @@ -30,7 +30,7 @@ class SliceWidget(SingleAxesWidget): def __init__( self, napari_viewer: napari.viewer.Viewer, - parent: Optional[QWidget] = None, + parent: QWidget | None = None, ): # Setup figure/axes super().__init__(napari_viewer, parent=parent) diff --git a/src/napari_matplotlib/util.py b/src/napari_matplotlib/util.py index ed99425..c54b60a 100644 --- a/src/napari_matplotlib/util.py +++ b/src/napari_matplotlib/util.py @@ -1,4 +1,3 @@ -from typing import Optional, Union from warnings import warn import napari.qt @@ -12,7 +11,7 @@ class Interval: An integer interval. """ - def __init__(self, lower_bound: Optional[int], upper_bound: Optional[int]): + def __init__(self, lower_bound: int | None, upper_bound: int | None): """ Parameters ---------- @@ -48,7 +47,7 @@ def __contains__(self, val: int) -> bool: return True @property - def _helper_text(self) -> Optional[str]: + def _helper_text(self) -> str | None: """ Helper text for widgets. """ @@ -86,9 +85,7 @@ def _has_id(nodes: list[tinycss2.ast.Node], id_name: str) -> bool: ) -def _get_dimension( - nodes: list[tinycss2.ast.Node], id_name: str -) -> Union[int, None]: +def _get_dimension(nodes: list[tinycss2.ast.Node], id_name: str) -> int | None: """ Get the value of the DimensionToken for the IdentToken `id_name`. From e7c49451e5e1b25ccef43ddba054ad4a23328e99 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 24 May 2024 14:43:04 +0100 Subject: [PATCH 043/101] Fix doc link --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8e3e859..ff2e60c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,7 +4,7 @@ Changelog ----- Dependencies ~~~~~~~~~~~~ -napari-matplotlib now adheres to `SPEC 0`_, and has: +napari-matplotlib now adheres to `SPEC 0 `_, and has: - Dropped support for Python 3.9 - Added support for Python 3.12 - Added a minimum required numpy verison of 1.23 From 1818110f3a04f9948b28ca789dd34eb7e7369674 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 24 May 2024 14:46:43 +0100 Subject: [PATCH 044/101] Don't fail if codecov fails --- .github/workflows/test_and_deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 94cf1e9..5f9ed4b 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -64,7 +64,7 @@ jobs: if: github.event_name != 'merge_group' with: token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: true + fail_ci_if_error: false From 687ba2a279f0f1f5ef80b59258fe0caf6b8aa7fa Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 24 May 2024 14:16:47 +0100 Subject: [PATCH 045/101] Add some scientific Python repo recommendatiosn --- .github/workflows/test_and_deploy.yml | 4 ++++ pyproject.toml | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 5f9ed4b..2a8b731 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -12,6 +12,10 @@ on: workflow_dispatch: merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: test: name: ${{ matrix.platform }} py${{ matrix.python-version }} diff --git a/pyproject.toml b/pyproject.toml index bbec409..5a865b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools", "wheel", "setuptools_scm"] +requires = ["setuptools", "setuptools_scm"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] @@ -16,7 +16,11 @@ filterwarnings = [ "ignore:Pickle, copy, and deepcopy support will be removed from itertools in Python 3.14.:DeprecationWarning" ] qt_api = "pyqt6" -addopts = "--mpl --mpl-baseline-relative" +addopts = ["--mpl", "--mpl-baseline-relative", "--strict-config", "--strict-markers", "-ra"] +minversion = "7" +testpaths = ["src/napari_matplotlib/tests"] +log_cli_level = "INFO" +xfail_strict = true [tool.black] line-length = 79 From 33a71d3231841b6452d677e2a94d0792e4ee3ffe Mon Sep 17 00:00:00 2001 From: David Stansby Date: Sat, 25 May 2024 09:29:58 +0100 Subject: [PATCH 046/101] Some more repo-review suggestions --- .pre-commit-config.yaml | 2 +- docs/conf.py | 1 + pyproject.toml | 12 ++++++++---- src/napari_matplotlib/histogram.py | 2 +- src/napari_matplotlib/tests/test_util.py | 2 +- src/napari_matplotlib/util.py | 9 +++++++-- 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 40048ea..79342e2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - - repo: https://github.com/psf/black + - repo: https://github.com/psf/black-pre-commit-mirror rev: 24.4.2 hooks: - id: black diff --git a/docs/conf.py b/docs/conf.py index 2818f02..f153383 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -69,6 +69,7 @@ def napari_scraper(block, block_vars, gallery_conf): # type: ignore[no-untyped- for win, img_path in zip( reversed(napari._qt.qt_main_window._QtMainWindow._instances), imgpath_iter, + strict=False, ): img_paths.append(img_path) win._window.screenshot(img_path, canvas_only=False) diff --git a/pyproject.toml b/pyproject.toml index 5a865b0..05f7df6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,10 @@ line_length = 79 [tool.ruff] target-version = "py310" -select = ["I", "UP", "F", "E", "W", "D"] +fix = true + +[tool.ruff.lint] +select = ["B", "I", "UP", "F", "E", "W", "D"] ignore = [ "D100", # Missing docstring in public module "D104", # Missing docstring in public package @@ -41,14 +44,13 @@ ignore = [ "D401", # First line of docstring should be in imperative mood ] -fix = true -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "docs/*" = ["D"] "examples/*" = ["D"] "src/napari_matplotlib/tests/*" = ["D"] -[tool.ruff.pydocstyle] +[tool.ruff.lint.pydocstyle] convention = "numpy" [tool.mypy] @@ -59,6 +61,8 @@ disallow_subclassing_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/*", diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index a0b6e09..adbbae6 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -60,7 +60,7 @@ def on_update_layers(self) -> None: def _update_contrast_lims(self) -> None: for lim, line in zip( - self.layers[0].contrast_limits, self._contrast_lines + self.layers[0].contrast_limits, self._contrast_lines, strict=False ): line.set_xdata(lim) diff --git a/src/napari_matplotlib/tests/test_util.py b/src/napari_matplotlib/tests/test_util.py index 918e763..e966cc2 100644 --- a/src/napari_matplotlib/tests/test_util.py +++ b/src/napari_matplotlib/tests/test_util.py @@ -26,7 +26,7 @@ def test_interval(): assert 10 not in interval with pytest.raises(ValueError, match="must be an integer"): - "string" in interval # type: ignore + assert "string" in interval # type: ignore[operator] with pytest.raises(ValueError, match="must be <= upper_bound"): Interval(5, 3) diff --git a/src/napari_matplotlib/util.py b/src/napari_matplotlib/util.py index c54b60a..8d4150c 100644 --- a/src/napari_matplotlib/util.py +++ b/src/napari_matplotlib/util.py @@ -94,14 +94,18 @@ def _get_dimension(nodes: list[tinycss2.ast.Node], id_name: str) -> int | None: None if no IdentToken is found. """ cleaned_nodes = [node for node in nodes if node.type != "whitespace"] - for name, _, value, _ in zip(*(iter(cleaned_nodes),) * 4): + for name, _, value, _ in zip(*(iter(cleaned_nodes),) * 4, strict=False): if ( name.type == "ident" and value.type == "dimension" and name.value == id_name ): return value.int_value - warn(f"Unable to find DimensionToken for {id_name}", RuntimeWarning) + warn( + f"Unable to find DimensionToken for {id_name}", + RuntimeWarning, + stacklevel=1, + ) return None @@ -134,6 +138,7 @@ def from_napari_css_get_size_of( f"Unable to find {qt_element_name} or unable to find its size in " f"the current Napari stylesheet, falling back to {fallback}", RuntimeWarning, + stacklevel=1, ) return QSize(*fallback) From 426a0f5f8401300b1c8c0e93c479e5559e1dec97 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Sat, 25 May 2024 10:36:31 +0100 Subject: [PATCH 047/101] 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 048/101] 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 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 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 cf9aae9e5f2ed6acc9182a5b53110abf5f4a19ad Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 17:37:35 +0000 Subject: [PATCH 049/101] [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.4.2 → v0.4.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.2...v0.4.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 79342e2..ad71481 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.4.2' + rev: 'v0.4.5' hooks: - id: ruff From dea3e6d9e66187c8b3212453c076b7ec7492cbc2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:41:38 +0000 Subject: [PATCH 050/101] [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.4.5 → v0.4.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.5...v0.4.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 ad71481..aefedf4 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.4.5' + rev: 'v0.4.7' hooks: - id: ruff From 34099bc0073e7e097773a328aaafae442a66889e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 17:44:38 +0000 Subject: [PATCH 051/101] [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.4.7 → v0.4.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.7...v0.4.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 aefedf4..c921bc0 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.4.7' + rev: 'v0.4.8' hooks: - id: ruff From 5990ef63ad6580adadacc48ddc9548aa7c3c6d82 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:41:36 +0000 Subject: [PATCH 052/101] [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.4.8 → v0.4.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.8...v0.4.9) --- .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 c921bc0..2fedfd8 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.4.8' + rev: 'v0.4.9' hooks: - id: ruff From 2ad5adf83523e6ae8474c7aa9b0397c8f5ff20ab Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:41:57 +0000 Subject: [PATCH 053/101] [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.4.9 → v0.4.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.9...v0.4.10) --- .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 2fedfd8..305eb83 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.4.9' + rev: 'v0.4.10' hooks: - id: ruff From c60ff0e3e2b829f55a8906f143189a72a1868ebd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 17:34:35 +0000 Subject: [PATCH 054/101] [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.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1) - [github.com/astral-sh/ruff-pre-commit: v0.4.10 → v0.5.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.10...v0.5.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 305eb83..ebae2d3 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.0 + rev: v1.10.1 hooks: - id: mypy additional_dependencies: [numpy, matplotlib] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.4.10' + rev: 'v0.5.1' hooks: - id: ruff From 3cb0fe7b7ddd391039b96e6fb6ff63cff3bbcfa9 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 12 Jul 2024 15:35:23 +0200 Subject: [PATCH 055/101] Pin max napari version --- docs/changelog.rst | 4 ++++ setup.cfg | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index ff2e60c..697e483 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,13 +1,17 @@ Changelog ========= + 2.0.2 ----- Dependencies ~~~~~~~~~~~~ napari-matplotlib now adheres to `SPEC 0 `_, and has: + - Dropped support for Python 3.9 - Added support for Python 3.12 - Added a minimum required numpy verison of 1.23 +- Pinned the maximum napari version to ``< 0.5``. + Version 3.0 of ``napari-matplotlib`` will introduce support for ``napari`` version 0.5. 2.0.1 ----- diff --git a/setup.cfg b/setup.cfg index 2b74504..073478a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ project_urls = packages = find: install_requires = matplotlib - napari + napari<0.5 numpy>=1.23 tinycss2 python_requires = >=3.10 From 70dc2567a17869f635d0b38189d2b07720717ed0 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 3 May 2024 15:16:10 +0100 Subject: [PATCH 056/101] Only update layers when selection is valid --- docs/changelog.rst | 6 ++++++ src/napari_matplotlib/base.py | 18 +++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 697e483..cab6ca6 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -13,6 +13,12 @@ napari-matplotlib now adheres to `SPEC 0 None: self._update_layers ) + @property + def _valid_layer_selection(self) -> bool: + """ + Return `True` if layer selection is valid. + """ + return self.n_selected_layers in self.n_layers_input and all( + isinstance(layer, self.input_layer_types) for layer in self.layers + ) + def _update_layers(self, event: napari.utils.events.Event) -> None: """ Update the ``layers`` attribute with currently selected layers and re-draw. """ self.layers = list(self.viewer.layers.selection) self.layers = sorted(self.layers, key=lambda layer: layer.name) - self.on_update_layers() + if self._valid_layer_selection: + self.on_update_layers() self._draw() def _draw(self) -> None: @@ -243,10 +253,7 @@ def _draw(self) -> None: with mplstyle.context(self.napari_theme_style_sheet): # everything should be done in the style context self.clear() - if self.n_selected_layers in self.n_layers_input and all( - isinstance(layer, self.input_layer_types) - for layer in self.layers - ): + if self._valid_layer_selection: self.draw() self.canvas.draw() # type: ignore[no-untyped-call] @@ -269,6 +276,7 @@ def on_update_layers(self) -> None: Called when the selected layers are updated. This is a no-op, and is intended for derived classes to override. + It is only called if a selected layer is one of the input layer types. """ From 72e5791a56dfddb7c1c5646f7d1c69cfb6d1d022 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Sat, 25 May 2024 09:36:29 +0100 Subject: [PATCH 057/101] Only set colors if x_axis_key is string --- src/napari_matplotlib/histogram.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index adbbae6..560676c 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -209,10 +209,12 @@ def draw(self) -> None: # get the colormap from the layer depending on its type if isinstance(self.layers[0], napari.layers.Points): colormap = self.layers[0].face_colormap - self.layers[0].face_color = self.x_axis_key + if self.x_axis_key: + self.layers[0].face_color = self.x_axis_key elif isinstance(self.layers[0], napari.layers.Vectors): colormap = self.layers[0].edge_colormap - self.layers[0].edge_color = self.x_axis_key + if self.x_axis_key: + self.layers[0].edge_color = self.x_axis_key else: colormap = None From 2874081c869b32b17977c4df54a7267d59a9a286 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Sat, 25 May 2024 09:44:58 +0100 Subject: [PATCH 058/101] Debugging --- src/napari_matplotlib/base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/napari_matplotlib/base.py b/src/napari_matplotlib/base.py index 9440b74..97f4146 100644 --- a/src/napari_matplotlib/base.py +++ b/src/napari_matplotlib/base.py @@ -229,6 +229,10 @@ def _valid_layer_selection(self) -> bool: """ Return `True` if layer selection is valid. """ + print(f"{self.n_selected_layers=}") + print(f"{self.n_layers_input=}") + print(f"{self.layers=}") + print(f"{self.input_layer_types=}") return self.n_selected_layers in self.n_layers_input and all( isinstance(layer, self.input_layer_types) for layer in self.layers ) From 3ebea2fdcbe375a079a445bdbffc0d504b62cece Mon Sep 17 00:00:00 2001 From: David Stansby Date: Sat, 25 May 2024 10:01:58 +0100 Subject: [PATCH 059/101] Put self._draw() in clause --- src/napari_matplotlib/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/napari_matplotlib/base.py b/src/napari_matplotlib/base.py index 97f4146..eb023c3 100644 --- a/src/napari_matplotlib/base.py +++ b/src/napari_matplotlib/base.py @@ -245,7 +245,7 @@ def _update_layers(self, event: napari.utils.events.Event) -> None: self.layers = sorted(self.layers, key=lambda layer: layer.name) if self._valid_layer_selection: self.on_update_layers() - self._draw() + self._draw() def _draw(self) -> None: """ From b01809f49893af89965e85a0c98fb3c0dbfc3b79 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 12 Jul 2024 15:52:02 +0200 Subject: [PATCH 060/101] Always call on_update_layers --- src/napari_matplotlib/base.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/napari_matplotlib/base.py b/src/napari_matplotlib/base.py index eb023c3..720333e 100644 --- a/src/napari_matplotlib/base.py +++ b/src/napari_matplotlib/base.py @@ -229,10 +229,6 @@ def _valid_layer_selection(self) -> bool: """ Return `True` if layer selection is valid. """ - print(f"{self.n_selected_layers=}") - print(f"{self.n_layers_input=}") - print(f"{self.layers=}") - print(f"{self.input_layer_types=}") return self.n_selected_layers in self.n_layers_input and all( isinstance(layer, self.input_layer_types) for layer in self.layers ) @@ -243,8 +239,8 @@ def _update_layers(self, event: napari.utils.events.Event) -> None: """ self.layers = list(self.viewer.layers.selection) self.layers = sorted(self.layers, key=lambda layer: layer.name) + self.on_update_layers() if self._valid_layer_selection: - self.on_update_layers() self._draw() def _draw(self) -> None: @@ -280,7 +276,6 @@ def on_update_layers(self) -> None: Called when the selected layers are updated. This is a no-op, and is intended for derived classes to override. - It is only called if a selected layer is one of the input layer types. """ From 8a545953b8de227fb4f0f79ffddf384ccadcac26 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 12 Jul 2024 16:00:36 +0200 Subject: [PATCH 061/101] Fix layer selection --- src/napari_matplotlib/histogram.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 560676c..2881cf7 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -55,8 +55,10 @@ def on_update_layers(self) -> None: Called when the selected layers are updated. """ super().on_update_layers() - for layer in self.viewer.layers: - layer.events.contrast_limits.connect(self._update_contrast_lims) + if self._valid_layer_selection: + self.layers[0].events.contrast_limits.connect( + self._update_contrast_lims + ) def _update_contrast_lims(self) -> None: for lim, line in zip( From 25b9b0ee979694d3fc33f9812115cc47b91efe35 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Fri, 12 Jul 2024 16:13:42 +0200 Subject: [PATCH 062/101] Update changelog --- docs/changelog.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index cab6ca6..54e6bba 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,13 @@ Changelog ========= +2.0.3 +----- +Bug fixes +~~~~~~~~~ +- Fix an error that happened when the histogram widget was open, but a layer that doesn't support + histogramming (e.g., a labels layer) was selected. + 2.0.2 ----- Dependencies @@ -13,12 +20,6 @@ napari-matplotlib now adheres to `SPEC 0 Date: Fri, 12 Jul 2024 16:24:48 +0200 Subject: [PATCH 063/101] 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 064/101] 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 065/101] 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 066/101] [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 067/101] [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 068/101] [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 069/101] 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 070/101] [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 071/101] [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 072/101] [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 073/101] 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 074/101] [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 075/101] [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 076/101] [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 077/101] [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 078/101] 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( From 37fce7f0cbeee2ff149196b574f02398b5d73b19 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 20:37:49 +0200 Subject: [PATCH 079/101] [pre-commit.ci] pre-commit autoupdate (#289) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0) - [github.com/astral-sh/ruff-pre-commit: v0.6.8 → v0.6.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.8...v0.6.9) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 e807909..924bc64 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-docstring-first - id: end-of-file-fixer @@ -24,7 +24,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.6.8' + rev: 'v0.6.9' hooks: - id: ruff From 9f49b7acc7bc9fbcb958b105272e4fe384bcaeca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 19:04:30 +0100 Subject: [PATCH 080/101] [pre-commit.ci] pre-commit autoupdate (#290) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black-pre-commit-mirror: 24.8.0 → 24.10.0](https://github.com/psf/black-pre-commit-mirror/compare/24.8.0...24.10.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 924bc64..ff538b9 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.8.0 + rev: 24.10.0 hooks: - id: black From b5178753a74158fb256852edf8a851a8598e3fe0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:33:49 +0000 Subject: [PATCH 081/101] [pre-commit.ci] pre-commit autoupdate (#291) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.11.2 → v1.13.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.2...v1.13.0) - [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.9...v0.7.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 ff538b9..761bf7b 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.2 + rev: v1.13.0 hooks: - id: mypy additional_dependencies: [numpy, matplotlib] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.6.9' + rev: 'v0.7.1' hooks: - id: ruff From 20cef6e780527a9d943f9306d79f7a51a90a5acd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:54:57 +0000 Subject: [PATCH 082/101] [pre-commit.ci] pre-commit autoupdate (#292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.1 → v0.7.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.1...v0.7.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 761bf7b..dea49b6 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.7.1' + rev: 'v0.7.2' hooks: - id: ruff From ced5d9da6eaf47d00d66cfd7f0b3c4273ed73150 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 17:48:49 +0000 Subject: [PATCH 083/101] [pre-commit.ci] pre-commit autoupdate (#293) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.2 → v0.7.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.2...v0.7.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 dea49b6..3e5d679 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.7.2' + rev: 'v0.7.3' hooks: - id: ruff From 89749f3f7b24be038fafe2e224d70274a741dabd Mon Sep 17 00:00:00 2001 From: Sam Cunliffe Date: Tue, 4 Feb 2025 14:42:30 +0000 Subject: [PATCH 084/101] Update actions to latest versions. --- .github/workflows/docs.yml | 4 ++-- .github/workflows/test_and_deploy.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e1a8621..965463c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,9 +17,9 @@ jobs: name: Build & Upload Artifact runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v5 with: python-version: "3.10" diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 2a8b731..3709ff5 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -27,10 +27,10 @@ jobs: python-version: ['3.10', '3.11', '3.12'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} From 71280f645904d19d16437ab648efe7efe3a27509 Mon Sep 17 00:00:00 2001 From: Sam Cunliffe Date: Tue, 4 Feb 2025 14:45:03 +0000 Subject: [PATCH 085/101] Missed a few. --- .github/workflows/docs.yml | 2 +- .github/workflows/test_and_deploy.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 965463c..fda82b6 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -49,7 +49,7 @@ jobs: needs: build-docs if: contains(github.ref, 'tags') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/download-artifact@v4.1.7 with: name: docs diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 3709ff5..69e2e2a 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -54,7 +54,7 @@ jobs: run: python -m tox - name: Upload pytest test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: pytest-results-${{ matrix.platform }} py${{ matrix.python-version }} path: reports/ @@ -83,9 +83,9 @@ jobs: permissions: id-token: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: "3.x" - name: Install build From 72a4a7f6cbcf420a2441a8d000f3c1a7c2f45fea Mon Sep 17 00:00:00 2001 From: Sam Cunliffe Date: Tue, 4 Feb 2025 14:46:35 +0000 Subject: [PATCH 086/101] Upload docs. --- .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 fda82b6..67dd158 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -38,7 +38,7 @@ jobs: working-directory: ./docs - name: Upload artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docs path: docs/_build From 80c782af77ff9334f92bbb3a2f1f11a9a33c546b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 17:29:38 +0000 Subject: [PATCH 087/101] [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.10.0 → 25.1.0](https://github.com/psf/black-pre-commit-mirror/compare/24.10.0...25.1.0) - [github.com/pre-commit/mirrors-mypy: v1.13.0 → v1.15.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.13.0...v1.15.0) - [github.com/astral-sh/ruff-pre-commit: v0.7.3 → v0.9.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.3...v0.9.9) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e5d679..fb1a8ea 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.10.0 + rev: 25.1.0 hooks: - id: black @@ -17,14 +17,14 @@ repos: - id: napari-plugin-checks - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.13.0 + rev: v1.15.0 hooks: - id: mypy additional_dependencies: [numpy, matplotlib] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: 'v0.7.3' + rev: 'v0.9.9' hooks: - id: ruff From 5f1129e441d004d636a5067d1107e3e42b90241b Mon Sep 17 00:00:00 2001 From: Sam Cunliffe Date: Tue, 4 Mar 2025 16:47:48 +0000 Subject: [PATCH 088/101] Ignore typing for now. --- src/napari_matplotlib/histogram.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index acdd840..f874f0c 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -28,7 +28,7 @@ def _get_bins( data: npt.NDArray[Any], num_bins: int = 100, -) -> npt.NDArray[Any]: +) -> npt.NDArray[np.floating]: """Create evenly spaced bins with a given interval. Parameters @@ -161,13 +161,13 @@ def draw(self) -> None: for i, c in enumerate("rgb"): self.axes.hist( data[..., i].ravel(), - bins=bins.tolist(), + bins=bins.tolist(), # type: ignore[arg-type] label=c, histtype="step", color=_COLORS[c], ) else: - self.axes.hist(data.ravel(), bins=bins.tolist(), label=layer.name) + self.axes.hist(data.ravel(), bins=bins.tolist(), label=layer.name) # type: ignore[arg-type] self._contrast_lines = [ self.axes.axvline(lim, color="white") @@ -297,7 +297,7 @@ def draw(self) -> None: bins = _get_bins(data) - _, bins, patches = self.axes.hist(data, bins=bins.tolist()) + _, bins, patches = self.axes.hist(data, bins=bins.tolist()) # type: ignore[arg-type] patches = cast(BarContainer, patches) # recolor the histogram plot From 3dd313761ef430016d8a90cd0e70ce54eb9045fe Mon Sep 17 00:00:00 2001 From: Sam Cunliffe Date: Tue, 4 Mar 2025 17:16:23 +0000 Subject: [PATCH 089/101] Create dependabot.yml --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..64284b9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +--- +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" From 439dc67314d483d022b33eb4d544c8efdab00ec9 Mon Sep 17 00:00:00 2001 From: Sam Cunliffe Date: Tue, 4 Mar 2025 17:27:30 +0000 Subject: [PATCH 090/101] Add group so all updates come in a single PR. --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 64284b9..af3b9f0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,3 +5,7 @@ updates: directory: "/" schedule: interval: "monthly" + groups: + github-actions: + patterns: + - "*" From 64e5c7095ee22f071d316e349a4d0e8f59654ac0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 17:33:13 +0000 Subject: [PATCH 091/101] Bump the github-actions group with 2 updates Bumps the github-actions group with 2 updates: [actions/download-artifact](https://github.com/actions/download-artifact) and [codecov/codecov-action](https://github.com/codecov/codecov-action). Updates `actions/download-artifact` from 4.1.7 to 4.1.9 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.7...v4.1.9) Updates `codecov/codecov-action` from 4 to 5 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- .github/workflows/test_and_deploy.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 67dd158..e0e477a 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@v4 - - uses: actions/download-artifact@v4.1.7 + - uses: actions/download-artifact@v4.1.9 with: name: docs diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 69e2e2a..77853f7 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -62,7 +62,7 @@ jobs: if: ${{ always() }} - name: Coverage - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 # Don't run coverage on merge queue CI to avoid duplicating reports # to codecov. See https://github.com/matplotlib/napari-matplotlib/issues/155 if: github.event_name != 'merge_group' From 067fd10afa65c3f7379ffe7a6f701cee517acff4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 17:22:39 +0000 Subject: [PATCH 092/101] [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.9.9 → v0.9.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.9...v0.9.10) --- .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 fb1a8ea..0547e1d 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.9.9' + rev: 'v0.9.10' hooks: - id: ruff From 228458957c7fa2fd0afcbef250a1627001a08ce7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 17:32:41 +0000 Subject: [PATCH 093/101] [pre-commit.ci] pre-commit autoupdate (#299) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.10 → v0.11.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.10...v0.11.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 0547e1d..cb440b3 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.9.10' + rev: 'v0.11.0' hooks: - id: ruff From 2d6a88650595456cc6c8901d03281c9cf99351cc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 18:13:32 +0000 Subject: [PATCH 094/101] [pre-commit.ci] pre-commit autoupdate (#300) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.0 → v0.11.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.0...v0.11.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 cb440b3..733413d 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.11.0' + rev: 'v0.11.2' hooks: - id: ruff From 1fb935f7a09e22db18f3dbd6f22ab6d6b393b9f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 13:03:27 +0100 Subject: [PATCH 095/101] Bump actions/download-artifact in the github-actions group (#301) Bumps the github-actions group with 1 update: [actions/download-artifact](https://github.com/actions/download-artifact). Updates `actions/download-artifact` from 4.1.9 to 4.2.1 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.9...v4.2.1) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: 4.2.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 e0e477a..b26b499 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@v4 - - uses: actions/download-artifact@v4.1.9 + - uses: actions/download-artifact@v4.2.1 with: name: docs From b81aa2d3a7e14efce9089e5a4d26a88c95ef72ae Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 21:23:51 +0100 Subject: [PATCH 096/101] [pre-commit.ci] pre-commit autoupdate (#302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.2 → v0.11.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.2...v0.11.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 733413d..a172dab 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.11.2' + rev: 'v0.11.4' hooks: - id: ruff From e13454a105c55f1f489222cb5bf247d21cf5e536 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:42:39 +0100 Subject: [PATCH 097/101] [pre-commit.ci] pre-commit autoupdate (#303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.4 → v0.11.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.4...v0.11.6) * Remove type:ignore flags for bins in histogram.py. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sam Cunliffe --- .pre-commit-config.yaml | 2 +- src/napari_matplotlib/histogram.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a172dab..bd929a1 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.11.4' + rev: 'v0.11.6' hooks: - id: ruff diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index f874f0c..85bba9d 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -161,13 +161,13 @@ def draw(self) -> None: for i, c in enumerate("rgb"): self.axes.hist( data[..., i].ravel(), - bins=bins.tolist(), # type: ignore[arg-type] + bins=bins.tolist(), label=c, histtype="step", color=_COLORS[c], ) else: - self.axes.hist(data.ravel(), bins=bins.tolist(), label=layer.name) # type: ignore[arg-type] + self.axes.hist(data.ravel(), bins=bins.tolist(), label=layer.name) self._contrast_lines = [ self.axes.axvline(lim, color="white") @@ -297,7 +297,7 @@ def draw(self) -> None: bins = _get_bins(data) - _, bins, patches = self.axes.hist(data, bins=bins.tolist()) # type: ignore[arg-type] + _, bins, patches = self.axes.hist(data, bins=bins.tolist()) patches = cast(BarContainer, patches) # recolor the histogram plot From c8fe163f25a3b31d4aa2f640e3756fc0302c45e3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 06:41:45 +0100 Subject: [PATCH 098/101] [pre-commit.ci] pre-commit autoupdate (#304) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.6 → v0.11.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.6...v0.11.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 bd929a1..81613d0 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.11.6' + rev: 'v0.11.7' hooks: - id: ruff From f7a33364e2367334404ef3fc7d94a119f586c1d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 10:52:55 +0100 Subject: [PATCH 099/101] Bump actions/download-artifact in the github-actions group (#305) Bumps the github-actions group with 1 update: [actions/download-artifact](https://github.com/actions/download-artifact). Updates `actions/download-artifact` from 4.2.1 to 4.3.0 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.2.1...v4.3.0) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: 4.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 b26b499..55eb1aa 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@v4 - - uses: actions/download-artifact@v4.2.1 + - uses: actions/download-artifact@v4.3.0 with: name: docs From 912ede93ae95936c5e8e5b654a10dd811c290933 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 09:26:19 +0100 Subject: [PATCH 100/101] [pre-commit.ci] pre-commit autoupdate (#306) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.7 → v0.11.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.7...v0.11.8) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 81613d0..e1b0baf 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.11.7' + rev: 'v0.11.8' hooks: - id: ruff From db2be01b230dcfcec984660ba42625e165c907de Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 06:13:27 +0100 Subject: [PATCH 101/101] [pre-commit.ci] pre-commit autoupdate (#307) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.8 → v0.11.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.8...v0.11.9) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .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 e1b0baf..e592dea 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.11.8' + rev: 'v0.11.9' hooks: - id: ruff