From d5f5872e0ac6ea4cbdfabd3949617c9d9c914692 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Mon, 15 May 2023 09:09:47 +0100 Subject: [PATCH 1/6] Add strict QT mode to tests --- src/napari_matplotlib/tests/conftest.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/napari_matplotlib/tests/conftest.py b/src/napari_matplotlib/tests/conftest.py index 06ed51f6..0788292d 100644 --- a/src/napari_matplotlib/tests/conftest.py +++ b/src/napari_matplotlib/tests/conftest.py @@ -1,3 +1,5 @@ +import os + import numpy as np import pytest from skimage import data @@ -22,3 +24,17 @@ def astronaut_data(): @pytest.fixture def brain_data(): return data.brain(), {"rgb": False} + + +@pytest.fixture(autouse=True, scope="session") +def set_strict_qt(): + env_var = "NAPARI_STRICT_QT" + old_val = os.environ.get(env_var) + os.environ[env_var] = "1" + # Run tests + yield + # Reset to original value + if old_val is not None: + os.environ[env_var] = old_val + else: + del os.environ[env_var] From 00b69c129546df9104bcf07d29aabbcf92e8de29 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Mon, 15 May 2023 10:15:44 +0100 Subject: [PATCH 2/6] Add parent kwargs Fix parent typing Fix QWidget import --- src/napari_matplotlib/base.py | 12 ++++++++---- src/napari_matplotlib/histogram.py | 17 +++++++++++------ src/napari_matplotlib/scatter.py | 26 ++++++++++++++++++-------- src/napari_matplotlib/slice.py | 12 ++++++++---- 4 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/napari_matplotlib/base.py b/src/napari_matplotlib/base.py index b69d0310..0368e7ef 100644 --- a/src/napari_matplotlib/base.py +++ b/src/napari_matplotlib/base.py @@ -1,6 +1,6 @@ import os from pathlib import Path -from typing import List, Tuple +from typing import List, Optional, Tuple import napari from matplotlib.axes import Axes @@ -43,8 +43,12 @@ class NapariMPLWidget(QWidget): List of currently selected napari layers. """ - def __init__(self, napari_viewer: napari.viewer.Viewer): - super().__init__() + def __init__( + self, + napari_viewer: napari.viewer.Viewer, + parent: Optional[QWidget] = None, + ): + super().__init__(parent=parent) self.viewer = napari_viewer self.canvas = FigureCanvas() @@ -52,7 +56,7 @@ def __init__(self, napari_viewer: napari.viewer.Viewer): self.canvas.figure.patch.set_facecolor("none") self.canvas.figure.set_layout_engine("constrained") self.toolbar = NapariNavigationToolbar( - self.canvas, self + self.canvas, parent=self ) # type: ignore[no-untyped-call] self._replace_toolbar_icons() diff --git a/src/napari_matplotlib/histogram.py b/src/napari_matplotlib/histogram.py index 7e863826..04d91ed1 100644 --- a/src/napari_matplotlib/histogram.py +++ b/src/napari_matplotlib/histogram.py @@ -1,13 +1,14 @@ +from typing import Optional + +import napari import numpy as np +from qtpy.QtWidgets import QWidget from .base import NapariMPLWidget +from .util import Interval __all__ = ["HistogramWidget"] -import napari - -from .util import Interval - _COLORS = {"r": "tab:red", "g": "tab:green", "b": "tab:blue"} @@ -19,8 +20,12 @@ class HistogramWidget(NapariMPLWidget): n_layers_input = Interval(1, 1) input_layer_types = (napari.layers.Image,) - def __init__(self, napari_viewer: napari.viewer.Viewer): - super().__init__(napari_viewer) + def __init__( + self, + napari_viewer: napari.viewer.Viewer, + parent: Optional[QWidget] = None, + ): + super().__init__(napari_viewer, parent=parent) self.add_single_axes() self.update_layers(None) diff --git a/src/napari_matplotlib/scatter.py b/src/napari_matplotlib/scatter.py index 405b7b09..1ce34493 100644 --- a/src/napari_matplotlib/scatter.py +++ b/src/napari_matplotlib/scatter.py @@ -4,6 +4,7 @@ import numpy.typing as npt from magicgui import magicgui from magicgui.widgets import ComboBox +from qtpy.QtWidgets import QWidget from .base import NapariMPLWidget from .util import Interval @@ -20,8 +21,12 @@ class ScatterBaseWidget(NapariMPLWidget): # the scatter is plotted as a 2D histogram _threshold_to_switch_to_histogram = 500 - def __init__(self, napari_viewer: napari.viewer.Viewer): - super().__init__(napari_viewer) + def __init__( + self, + napari_viewer: napari.viewer.Viewer, + parent: Optional[QWidget] = None, + ): + super().__init__(napari_viewer, parent=parent) self.add_single_axes() self.update_layers(None) @@ -113,16 +118,21 @@ class FeaturesScatterWidget(ScatterBaseWidget): napari.layers.Vectors, ) - def __init__(self, napari_viewer: napari.viewer.Viewer): - super().__init__(napari_viewer) - self._key_selection_widget = magicgui( + def __init__( + self, + napari_viewer: napari.viewer.Viewer, + parent: Optional[QWidget] = None, + ): + super().__init__(napari_viewer, parent=parent) + self._key_selection_function_gui = magicgui( self._set_axis_keys, x_axis_key={"choices": self._get_valid_axis_keys}, y_axis_key={"choices": self._get_valid_axis_keys}, call_button="plot", ) - - self.layout().addWidget(self._key_selection_widget.native) + _key_selection_widget = self._key_selection_function_gui.native + _key_selection_widget.setParent(self) + self.layout().addWidget(_key_selection_widget) @property def x_axis_key(self) -> Optional[str]: @@ -231,7 +241,7 @@ def _on_update_layers(self) -> None: Called when the layer selection changes by ``self.update_layers()``. """ if hasattr(self, "_key_selection_widget"): - self._key_selection_widget.reset_choices() + self._key_selection_function_gui.reset_choices() # reset the axis keys self._x_axis_key = None diff --git a/src/napari_matplotlib/slice.py b/src/napari_matplotlib/slice.py index 4e22bad8..78e7841c 100644 --- a/src/napari_matplotlib/slice.py +++ b/src/napari_matplotlib/slice.py @@ -1,9 +1,9 @@ -from typing import Any, Dict, Tuple +from typing import Any, Dict, Optional, Tuple import napari import numpy as np import numpy.typing as npt -from qtpy.QtWidgets import QComboBox, QHBoxLayout, QLabel, QSpinBox +from qtpy.QtWidgets import QComboBox, QHBoxLayout, QLabel, QSpinBox, QWidget from .base import NapariMPLWidget from .util import Interval @@ -22,9 +22,13 @@ class SliceWidget(NapariMPLWidget): n_layers_input = Interval(1, 1) input_layer_types = (napari.layers.Image,) - def __init__(self, napari_viewer: napari.viewer.Viewer): + def __init__( + self, + napari_viewer: napari.viewer.Viewer, + parent: Optional[QWidget] = None, + ): # Setup figure/axes - super().__init__(napari_viewer) + super().__init__(napari_viewer, parent=parent) self.add_single_axes() button_layout = QHBoxLayout() From cf6118c92f98d0e7a5c4f0ca2f76ad3ea06d62cc Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 17 May 2023 13:19:12 +0100 Subject: [PATCH 3/6] Ignore missing mypy imports --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index aec6e9af..61dc3114 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ disallow_incomplete_defs = true disallow_untyped_defs = true no_implicit_reexport = true warn_return_any = false # TODO: fix +ignore_missing_imports = true [[tool.mypy.overrides]] module = [ From 452b848aaba1685ea28d13b7696552ddc5869d03 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 17 May 2023 13:53:17 +0100 Subject: [PATCH 4/6] Put test commands together --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 5a8cf188..6f7a4823 100644 --- a/tox.ini +++ b/tox.ini @@ -12,5 +12,5 @@ python = [testenv] extras = testing commands = - - python -c 'from skimage import data; data.brain()' - - python -m pytest --mpl -v --color=yes --cov=napari_matplotlib --cov-report=xml + python -c 'from skimage import data; data.brain()' + python -m pytest --mpl -v --color=yes --cov=napari_matplotlib --cov-report=xml From beec547df8040d001d07ba311930ce5f472068d4 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 17 May 2023 16:23:26 +0100 Subject: [PATCH 5/6] Re-write scatterfeatures without magicgui --- src/napari_matplotlib/scatter.py | 84 +++++++++++++++----------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/src/napari_matplotlib/scatter.py b/src/napari_matplotlib/scatter.py index 1ce34493..3cc7b169 100644 --- a/src/napari_matplotlib/scatter.py +++ b/src/napari_matplotlib/scatter.py @@ -1,10 +1,8 @@ -from typing import Any, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, Union import napari import numpy.typing as npt -from magicgui import magicgui -from magicgui.widgets import ComboBox -from qtpy.QtWidgets import QWidget +from qtpy.QtWidgets import QComboBox, QLabel, QVBoxLayout, QWidget from .base import NapariMPLWidget from .util import Interval @@ -27,9 +25,7 @@ def __init__( parent: Optional[QWidget] = None, ): super().__init__(napari_viewer, parent=parent) - self.add_single_axes() - self.update_layers(None) def clear(self) -> None: """ @@ -124,54 +120,51 @@ def __init__( parent: Optional[QWidget] = None, ): super().__init__(napari_viewer, parent=parent) - self._key_selection_function_gui = magicgui( - self._set_axis_keys, - x_axis_key={"choices": self._get_valid_axis_keys}, - y_axis_key={"choices": self._get_valid_axis_keys}, - call_button="plot", - ) - _key_selection_widget = self._key_selection_function_gui.native - _key_selection_widget.setParent(self) - self.layout().addWidget(_key_selection_widget) + + self.layout().addLayout(QVBoxLayout()) + + self._selectors: Dict[str, QComboBox] = {} + for dim in ["x", "y"]: + self._selectors[dim] = QComboBox() + # Re-draw when combo boxes are updated + self._selectors[dim].currentTextChanged.connect(self._draw) + + self.layout().addWidget(QLabel(f"{dim}-axis:")) + self.layout().addWidget(self._selectors[dim]) + + self.update_layers(None) @property - def x_axis_key(self) -> Optional[str]: + def x_axis_key(self) -> Union[str, None]: """ Key to access x axis data from the FeaturesTable. """ - return self._x_axis_key + if self._selectors["x"].count() == 0: + return None + else: + return self._selectors["x"].currentText() @x_axis_key.setter - def x_axis_key(self, key: Optional[str]) -> None: - self._x_axis_key = key + def x_axis_key(self, key: str) -> None: + self._selectors["x"].setCurrentText(key) self._draw() @property - def y_axis_key(self) -> Optional[str]: + def y_axis_key(self) -> Union[str, None]: """ Key to access y axis data from the FeaturesTable. """ - return self._y_axis_key + if self._selectors["y"].count() == 0: + return None + else: + return self._selectors["y"].currentText() @y_axis_key.setter - def y_axis_key(self, key: Optional[str]) -> None: - """ - Set the y-axis key. - """ - self._y_axis_key = key - self._draw() - - def _set_axis_keys(self, x_axis_key: str, y_axis_key: str) -> None: - """ - Set both axis keys and then redraw the plot. - """ - self._x_axis_key = x_axis_key - self._y_axis_key = y_axis_key + def y_axis_key(self, key: str) -> None: + self._selectors["y"].setCurrentText(key) self._draw() - def _get_valid_axis_keys( - self, combo_widget: Optional[ComboBox] = None - ) -> List[str]: + def _get_valid_axis_keys(self) -> List[str]: """ Get the valid axis keys from the layer FeatureTable. @@ -195,11 +188,12 @@ def _ready_to_scatter(self) -> bool: return False feature_table = self.layers[0].features + valid_keys = self._get_valid_axis_keys() return ( feature_table is not None and len(feature_table) > 0 - and self.x_axis_key is not None - and self.y_axis_key is not None + and self.x_axis_key in valid_keys + and self.y_axis_key in valid_keys ) def draw(self) -> None: @@ -240,9 +234,9 @@ def _on_update_layers(self) -> None: """ Called when the layer selection changes by ``self.update_layers()``. """ - if hasattr(self, "_key_selection_widget"): - self._key_selection_function_gui.reset_choices() - - # reset the axis keys - self._x_axis_key = None - self._y_axis_key = None + # Clear combobox + for dim in ["x", "y"]: + while self._selectors[dim].count() > 0: + self._selectors[dim].removeItem(0) + # Add keys for newly selected layer + self._selectors[dim].addItems(self._get_valid_axis_keys()) From d067f83306afec54f112953d6afb74efd8723025 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 17 May 2023 16:23:39 +0100 Subject: [PATCH 6/6] Add mypy python version --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 61dc3114..1944249c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ fix = true convention = "numpy" [tool.mypy] +python_version = "3.8" # Block below are checks that form part of mypy 'strict' mode warn_unused_configs = true warn_redundant_casts = true pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy