From 2bd22cc4f2c5f33b0f8ecf152f0b01a5988d8862 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Sun, 12 Sep 2021 15:37:26 +0200 Subject: [PATCH 01/27] Open QListWidget if len(img.scenes) > 1 --- napari_aicsimageio/core.py | 48 +++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index a32277a..bd2ad49 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -7,6 +7,9 @@ import xarray as xr from aicsimageio import AICSImage, exceptions, types from aicsimageio.dimensions import DimensionNames +from qtpy.QtWidgets import QListWidget +from qtpy.QtCore import Qt +from napari import Viewer ############################################################################### @@ -14,6 +17,24 @@ PathLike = Union[str, List[str]] ReaderFunction = Callable[[PathLike], List[LayerData]] +############################################################################### +# _get_viewer() function from https://github.com/napari/napari/issues/2202 +# Copyright (c) 2021 Jonas Windhager +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +def _get_viewer() -> Optional[Viewer]: + import inspect + + frame = inspect.currentframe().f_back + while frame: + instance = frame.f_locals.get("self") + if instance is not None and isinstance(instance, Viewer): + return instance + frame = frame.f_back + return None + + ############################################################################### @@ -41,6 +62,17 @@ def _get_full_image_data(img: AICSImage, in_memory: bool) -> Optional[xr.DataArr return None +def _get_scenes(img: AICSImage, in_memory: bool) -> Optional[xr.DataArray]: + list_widget = QListWidget() + for scene in img.scenes: + list_widget.addItem(scene) + viewer = _get_viewer() + # list_widget.currentItemChanged.connect(open_scene) + viewer.window.add_dock_widget([list_widget], area="right") + + return None + + def reader_function( path: PathLike, in_memory: bool, scene_name: Optional[str] = None ) -> Optional[List[LayerData]]: @@ -57,13 +89,17 @@ def reader_function( # Open file and get data img = AICSImage(path) - print( - f"AICSImageIO: Image contains {len(img.scenes)} scenes. " - f"napari-aicsimageio currently only supports loading first scene, " - f"will load scene: '{img.current_scene}'." - ) - data = _get_full_image_data(img, in_memory=in_memory) + if len(img.scenes) > 1: + print( + f"AICSImageIO: Image contains {len(img.scenes)} scenes. " + f"Supporting more than the first scene is a work in progress. " + f"Will show scenes, but load scene: '{img.current_scene}'." + ) + _get_scenes(img, in_memory=in_memory) + data = _get_full_image_data(img, in_memory=in_memory) + else: + data = _get_full_image_data(img, in_memory=in_memory) # Catch None data if data is None: From 2d71dfe8f9720b35e8846cc50ac37928a2e53411 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Sun, 12 Sep 2021 16:20:23 +0200 Subject: [PATCH 02/27] Refactor meta into a function --- napari_aicsimageio/core.py | 97 ++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index bd2ad49..ec6c878 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -67,12 +67,70 @@ def _get_scenes(img: AICSImage, in_memory: bool) -> Optional[xr.DataArray]: for scene in img.scenes: list_widget.addItem(scene) viewer = _get_viewer() - # list_widget.currentItemChanged.connect(open_scene) + + def open_scene(item): + scene = item.text() + img.set_scene(scene) + if DimensionNames.MosaicTile in img.reader.dims.order: + try: + if in_memory: + return img.reader.mosaic_xarray_data + else: + return img.reader.mosaic_xarray_dask_data + + # Catch reader does not support tile stitching + except NotImplementedError: + print( + "AICSImageIO: Mosaic tile stitching " + "not yet supported for this file format reader." + ) + else: + print(img.scene) + if in_memory: + return img.reader.xarray_data + else: + return img.reader.xarray_dask_data + + list_widget.currentItemChanged.connect(open_scene) viewer.window.add_dock_widget([list_widget], area="right") return None +def _get_meta(data, img): + # Metadata to provide with data + meta = {} + if DimensionNames.Channel in data.dims: + # Construct basic metadata + meta["name"] = data.coords[DimensionNames.Channel].data.tolist() + meta["channel_axis"] = data.dims.index(DimensionNames.Channel) + # Not multi-channel, use current scene as image name + else: + meta["name"] = img.reader.current_scene + # Handle samples / RGB + if DimensionNames.Samples in img.reader.dims.order: + meta["rgb"] = True + # Handle scales + scale: List[float] = [] + for dim in img.reader.dims.order: + if dim in [ + DimensionNames.SpatialX, + DimensionNames.SpatialY, + DimensionNames.SpatialZ, + ]: + scale_val = getattr(img.physical_pixel_sizes, dim) + print(scale_val) + if scale_val is not None: + scale.append(scale_val) + # Apply scales + if len(scale) > 0: + meta["scale"] = tuple(scale) + # Apply all other metadata + meta["metadata"] = {"ome_types": img.metadata} + + return meta + + def reader_function( path: PathLike, in_memory: bool, scene_name: Optional[str] = None ) -> Optional[List[LayerData]]: @@ -96,7 +154,7 @@ def reader_function( f"Supporting more than the first scene is a work in progress. " f"Will show scenes, but load scene: '{img.current_scene}'." ) - _get_scenes(img, in_memory=in_memory) + # data = _get_scenes(img, in_memory=in_memory) data = _get_full_image_data(img, in_memory=in_memory) else: data = _get_full_image_data(img, in_memory=in_memory) @@ -105,40 +163,7 @@ def reader_function( if data is None: return None else: - # Metadata to provide with data - meta = {} - if DimensionNames.Channel in data.dims: - # Construct basic metadata - meta["name"] = data.coords[DimensionNames.Channel].data.tolist() - meta["channel_axis"] = data.dims.index(DimensionNames.Channel) - - # Not multi-channel, use current scene as image name - else: - meta["name"] = img.reader.current_scene - - # Handle samples / RGB - if DimensionNames.Samples in img.reader.dims.order: - meta["rgb"] = True - - # Handle scales - scale: List[float] = [] - for dim in img.reader.dims.order: - if dim in [ - DimensionNames.SpatialX, - DimensionNames.SpatialY, - DimensionNames.SpatialZ, - ]: - scale_val = getattr(img.physical_pixel_sizes, dim) - if scale_val is not None: - scale.append(scale_val) - - # Apply scales - if len(scale) > 0: - meta["scale"] = tuple(scale) - - # Apply all other metadata - meta["metadata"] = {"ome_types": img.metadata} - + meta = _get_meta(data, img) return [(data.data, meta, "image")] From 6a619e6bb23c038bc20cc6c8358fb6a7e9f654d0 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Sun, 12 Sep 2021 17:20:22 +0200 Subject: [PATCH 03/27] Initial try at getting LayerData from list --- napari_aicsimageio/core.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index ec6c878..28cd325 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -67,6 +67,7 @@ def _get_scenes(img: AICSImage, in_memory: bool) -> Optional[xr.DataArray]: for scene in img.scenes: list_widget.addItem(scene) viewer = _get_viewer() + viewer.window.add_dock_widget([list_widget], area="right") def open_scene(item): scene = item.text() @@ -74,9 +75,9 @@ def open_scene(item): if DimensionNames.MosaicTile in img.reader.dims.order: try: if in_memory: - return img.reader.mosaic_xarray_data + data = img.reader.mosaic_xarray_data else: - return img.reader.mosaic_xarray_dask_data + data = img.reader.mosaic_xarray_dask_data # Catch reader does not support tile stitching except NotImplementedError: @@ -85,16 +86,14 @@ def open_scene(item): "not yet supported for this file format reader." ) else: - print(img.scene) if in_memory: - return img.reader.xarray_data + data = img.reader.xarray_data else: - return img.reader.xarray_dask_data + data = img.reader.xarray_dask_data + meta = _get_meta(data, img) + return [(data.data, meta, "image")] list_widget.currentItemChanged.connect(open_scene) - viewer.window.add_dock_widget([list_widget], area="right") - - return None def _get_meta(data, img): @@ -119,7 +118,6 @@ def _get_meta(data, img): DimensionNames.SpatialZ, ]: scale_val = getattr(img.physical_pixel_sizes, dim) - print(scale_val) if scale_val is not None: scale.append(scale_val) # Apply scales @@ -154,17 +152,16 @@ def reader_function( f"Supporting more than the first scene is a work in progress. " f"Will show scenes, but load scene: '{img.current_scene}'." ) - # data = _get_scenes(img, in_memory=in_memory) - data = _get_full_image_data(img, in_memory=in_memory) + _get_scenes(img, in_memory=in_memory) else: data = _get_full_image_data(img, in_memory=in_memory) - # Catch None data - if data is None: - return None - else: - meta = _get_meta(data, img) - return [(data.data, meta, "image")] + # Catch None data + if data is None: + return None + else: + meta = _get_meta(data, img) + return [(data.data, meta, "image")] def get_reader(path: PathLike, in_memory: bool) -> Optional[ReaderFunction]: From a7ecb91c4d5b72215d9108268d57311f123bda37 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Sun, 12 Sep 2021 17:42:50 +0200 Subject: [PATCH 04/27] Return [(None,)] and call add_image --- napari_aicsimageio/core.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index 28cd325..3d6dfd3 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -91,9 +91,14 @@ def open_scene(item): else: data = img.reader.xarray_dask_data meta = _get_meta(data, img) - return [(data.data, meta, "image")] + viewer.add_image( + data, + name=scene, + metadata=meta, + ) list_widget.currentItemChanged.connect(open_scene) + return None def _get_meta(data, img): @@ -153,6 +158,7 @@ def reader_function( f"Will show scenes, but load scene: '{img.current_scene}'." ) _get_scenes(img, in_memory=in_memory) + return [(None,)] else: data = _get_full_image_data(img, in_memory=in_memory) From 45041db62969ff24b4fd1ed60715e16470344c0a Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Sun, 12 Sep 2021 17:47:09 +0200 Subject: [PATCH 05/27] Ensure meta["scale"] is used --- napari_aicsimageio/core.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index 3d6dfd3..fd9424a 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -91,11 +91,7 @@ def open_scene(item): else: data = img.reader.xarray_dask_data meta = _get_meta(data, img) - viewer.add_image( - data, - name=scene, - metadata=meta, - ) + viewer.add_image(data, name=scene, metadata=meta, scale=meta["scale"]) list_widget.currentItemChanged.connect(open_scene) return None From 8e4b238a757449f9a554de2b55f5e3bfb116fb1f Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Sun, 12 Sep 2021 17:57:39 +0200 Subject: [PATCH 06/27] Add comments --- napari_aicsimageio/core.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index fd9424a..d0d609c 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -19,6 +19,7 @@ ############################################################################### # _get_viewer() function from https://github.com/napari/napari/issues/2202 +# To provide access to the napari viewer to make the dock widget # Copyright (c) 2021 Jonas Windhager # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. @@ -62,13 +63,16 @@ def _get_full_image_data(img: AICSImage, in_memory: bool) -> Optional[xr.DataArr return None +# Function to handle multi-scene files. def _get_scenes(img: AICSImage, in_memory: bool) -> Optional[xr.DataArray]: + # Create the list widget and populate with the scenes in the file list_widget = QListWidget() for scene in img.scenes: list_widget.addItem(scene) viewer = _get_viewer() - viewer.window.add_dock_widget([list_widget], area="right") + viewer.window.add_dock_widget([list_widget], area="right", name="Scene Selector") + # Function to create image layer from a scene selected in the list widget def open_scene(item): scene = item.text() img.set_scene(scene) @@ -97,8 +101,8 @@ def open_scene(item): return None +# Function to get Metadata to provide with data def _get_meta(data, img): - # Metadata to provide with data meta = {} if DimensionNames.Channel in data.dims: # Construct basic metadata @@ -147,13 +151,16 @@ def reader_function( # Open file and get data img = AICSImage(path) + # Check for multiple scenes if len(img.scenes) > 1: print( f"AICSImageIO: Image contains {len(img.scenes)} scenes. " f"Supporting more than the first scene is a work in progress. " - f"Will show scenes, but load scene: '{img.current_scene}'." + f"Select a scene from the list widget. There may be dragons!" ) + # Launch the list widget _get_scenes(img, in_memory=in_memory) + # Return an empty LayerData list, because Layers will be handled via the widget. HT Jonas Windhager return [(None,)] else: data = _get_full_image_data(img, in_memory=in_memory) From 76810375763414b6acd0f3712622853c27fb542d Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 09:24:22 +0200 Subject: [PATCH 07/27] Try Flake8 --- napari_aicsimageio/core.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index d0d609c..052aeee 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -8,7 +8,6 @@ from aicsimageio import AICSImage, exceptions, types from aicsimageio.dimensions import DimensionNames from qtpy.QtWidgets import QListWidget -from qtpy.QtCore import Qt from napari import Viewer ############################################################################### @@ -21,9 +20,9 @@ # _get_viewer() function from https://github.com/napari/napari/issues/2202 # To provide access to the napari viewer to make the dock widget # Copyright (c) 2021 Jonas Windhager -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# Licensed under MIT License + + def _get_viewer() -> Optional[Viewer]: import inspect @@ -160,7 +159,8 @@ def reader_function( ) # Launch the list widget _get_scenes(img, in_memory=in_memory) - # Return an empty LayerData list, because Layers will be handled via the widget. HT Jonas Windhager + # Return an empty LayerData list; ImgLayers will be handled via the widget. + # HT Jonas Windhager return [(None,)] else: data = _get_full_image_data(img, in_memory=in_memory) From 78edab761c91c52885848684111e4ff609e290ab Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 10:57:26 +0200 Subject: [PATCH 08/27] Use napari.current_viewer() --- napari_aicsimageio/core.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index 052aeee..2eda8c8 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -8,7 +8,9 @@ from aicsimageio import AICSImage, exceptions, types from aicsimageio.dimensions import DimensionNames from qtpy.QtWidgets import QListWidget -from napari import Viewer + +# from napari import Viewer +import napari ############################################################################### @@ -23,16 +25,16 @@ # Licensed under MIT License -def _get_viewer() -> Optional[Viewer]: - import inspect +# def _get_viewer() -> Optional[Viewer]: +# import inspect - frame = inspect.currentframe().f_back - while frame: - instance = frame.f_locals.get("self") - if instance is not None and isinstance(instance, Viewer): - return instance - frame = frame.f_back - return None +# frame = inspect.currentframe().f_back +# while frame: +# instance = frame.f_locals.get("self") +# if instance is not None and isinstance(instance, Viewer): +# return instance +# frame = frame.f_back +# return None ############################################################################### @@ -68,7 +70,7 @@ def _get_scenes(img: AICSImage, in_memory: bool) -> Optional[xr.DataArray]: list_widget = QListWidget() for scene in img.scenes: list_widget.addItem(scene) - viewer = _get_viewer() + viewer = napari.current_viewer() viewer.window.add_dock_widget([list_widget], area="right", name="Scene Selector") # Function to create image layer from a scene selected in the list widget From a69c9a66a02b8f9b245de4de590ece7dd75e48ed Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 11:19:59 +0200 Subject: [PATCH 09/27] Add & use scene indexes --- napari_aicsimageio/core.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index 2eda8c8..b95919e 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -65,18 +65,19 @@ def _get_full_image_data(img: AICSImage, in_memory: bool) -> Optional[xr.DataArr # Function to handle multi-scene files. -def _get_scenes(img: AICSImage, in_memory: bool) -> Optional[xr.DataArray]: +def _get_scenes(img: AICSImage, in_memory: bool) -> None: # Create the list widget and populate with the scenes in the file list_widget = QListWidget() - for scene in img.scenes: - list_widget.addItem(scene) + for i, scene in enumerate(img.scenes): + list_widget.addItem(f"{i} -- {scene}") viewer = napari.current_viewer() viewer.window.add_dock_widget([list_widget], area="right", name="Scene Selector") # Function to create image layer from a scene selected in the list widget def open_scene(item): - scene = item.text() - img.set_scene(scene) + scene_text = item.text() + scene_index = int(scene_text.split(" -- ")[0]) + img.set_scene(scene_index) if DimensionNames.MosaicTile in img.reader.dims.order: try: if in_memory: From cd8911c66329571be14732968844a413a874995d Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 11:42:09 +0200 Subject: [PATCH 10/27] Fix ImgLayer name --- napari_aicsimageio/core.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index b95919e..d1b63ff 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -69,14 +69,14 @@ def _get_scenes(img: AICSImage, in_memory: bool) -> None: # Create the list widget and populate with the scenes in the file list_widget = QListWidget() for i, scene in enumerate(img.scenes): - list_widget.addItem(f"{i} -- {scene}") + list_widget.addItem(f"{i} :: {scene}") viewer = napari.current_viewer() viewer.window.add_dock_widget([list_widget], area="right", name="Scene Selector") # Function to create image layer from a scene selected in the list widget def open_scene(item): scene_text = item.text() - scene_index = int(scene_text.split(" -- ")[0]) + scene_index = int(scene_text.split(" :: ")[0]) img.set_scene(scene_index) if DimensionNames.MosaicTile in img.reader.dims.order: try: @@ -97,7 +97,7 @@ def open_scene(item): else: data = img.reader.xarray_dask_data meta = _get_meta(data, img) - viewer.add_image(data, name=scene, metadata=meta, scale=meta["scale"]) + viewer.add_image(data, name=scene_text, metadata=meta, scale=meta["scale"]) list_widget.currentItemChanged.connect(open_scene) return None From aaa4b7771aeacea2090ac4798e2a17fc96e004ff Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 11:45:03 +0200 Subject: [PATCH 11/27] Fix lines and return none --- napari_aicsimageio/core.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index d1b63ff..5fee7f5 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -100,7 +100,6 @@ def open_scene(item): viewer.add_image(data, name=scene_text, metadata=meta, scale=meta["scale"]) list_widget.currentItemChanged.connect(open_scene) - return None # Function to get Metadata to provide with data @@ -110,12 +109,15 @@ def _get_meta(data, img): # Construct basic metadata meta["name"] = data.coords[DimensionNames.Channel].data.tolist() meta["channel_axis"] = data.dims.index(DimensionNames.Channel) + # Not multi-channel, use current scene as image name else: meta["name"] = img.reader.current_scene + # Handle samples / RGB if DimensionNames.Samples in img.reader.dims.order: meta["rgb"] = True + # Handle scales scale: List[float] = [] for dim in img.reader.dims.order: @@ -127,9 +129,11 @@ def _get_meta(data, img): scale_val = getattr(img.physical_pixel_sizes, dim) if scale_val is not None: scale.append(scale_val) + # Apply scales if len(scale) > 0: meta["scale"] = tuple(scale) + # Apply all other metadata meta["metadata"] = {"ome_types": img.metadata} From 1f7a28f468d27fdc72dc7b6d9b4718a3a72bfde9 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 11:54:36 +0200 Subject: [PATCH 12/27] Clean-up & comments --- napari_aicsimageio/core.py | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index 5fee7f5..df0fb17 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -8,8 +8,6 @@ from aicsimageio import AICSImage, exceptions, types from aicsimageio.dimensions import DimensionNames from qtpy.QtWidgets import QListWidget - -# from napari import Viewer import napari ############################################################################### @@ -18,25 +16,6 @@ PathLike = Union[str, List[str]] ReaderFunction = Callable[[PathLike], List[LayerData]] -############################################################################### -# _get_viewer() function from https://github.com/napari/napari/issues/2202 -# To provide access to the napari viewer to make the dock widget -# Copyright (c) 2021 Jonas Windhager -# Licensed under MIT License - - -# def _get_viewer() -> Optional[Viewer]: -# import inspect - -# frame = inspect.currentframe().f_back -# while frame: -# instance = frame.f_locals.get("self") -# if instance is not None and isinstance(instance, Viewer): -# return instance -# frame = frame.f_back -# return None - - ############################################################################### @@ -66,7 +45,8 @@ def _get_full_image_data(img: AICSImage, in_memory: bool) -> Optional[xr.DataArr # Function to handle multi-scene files. def _get_scenes(img: AICSImage, in_memory: bool) -> None: - # Create the list widget and populate with the scenes in the file + + # Create the list widget and populate with the ids & scenes in the file list_widget = QListWidget() for i, scene in enumerate(img.scenes): list_widget.addItem(f"{i} :: {scene}") @@ -76,6 +56,8 @@ def _get_scenes(img: AICSImage, in_memory: bool) -> None: # Function to create image layer from a scene selected in the list widget def open_scene(item): scene_text = item.text() + + # Use scene indexes to cover for duplicate names scene_index = int(scene_text.split(" :: ")[0]) img.set_scene(scene_index) if DimensionNames.MosaicTile in img.reader.dims.order: @@ -106,6 +88,7 @@ def open_scene(item): def _get_meta(data, img): meta = {} if DimensionNames.Channel in data.dims: + # Construct basic metadata meta["name"] = data.coords[DimensionNames.Channel].data.tolist() meta["channel_axis"] = data.dims.index(DimensionNames.Channel) @@ -161,11 +144,12 @@ def reader_function( if len(img.scenes) > 1: print( f"AICSImageIO: Image contains {len(img.scenes)} scenes. " - f"Supporting more than the first scene is a work in progress. " + f"Supporting more than the first scene is experimental. " f"Select a scene from the list widget. There may be dragons!" ) # Launch the list widget _get_scenes(img, in_memory=in_memory) + # Return an empty LayerData list; ImgLayers will be handled via the widget. # HT Jonas Windhager return [(None,)] From 1b3fa281b845538e9f3dd56f9da740e4bad44af4 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 13:05:26 +0200 Subject: [PATCH 13/27] change to napari[all] and v4.11 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d853762..da08ef3 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ requirements = [ "aicsimageio[all]~=4.0.2", "fsspec[http]", # no version pin, we pull from aicsimageio - "napari~=0.4.10", + "napari[all]~=0.4.11", "napari_plugin_engine~=0.1.4", ] From 196888bfc79cb7118bf6ff0458fea28441b64691 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 15:13:13 +0200 Subject: [PATCH 14/27] Fix scale in LIF test --- napari_aicsimageio/tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/napari_aicsimageio/tests/test_core.py b/napari_aicsimageio/tests/test_core.py index a6259a1..f6789fb 100644 --- a/napari_aicsimageio/tests/test_core.py +++ b/napari_aicsimageio/tests/test_core.py @@ -57,7 +57,7 @@ { "name": ["Gray", "Red", "Green", "Cyan"], "channel_axis": 1, - "scale": (4.984719055966396, 4.984719055966396), + "scale": (0.20061311154598827, 0.20061311154598827), }, ), ], From 308fc3ad1f8571fa6939953fc67a724448fcfd19 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 16:34:55 +0200 Subject: [PATCH 15/27] Add pytest-qt to test_req --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index da08ef3..5ba6055 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ "mypy>=0.800", "psutil>=5.7.0", "pytest>=5.4.3", + "pytest-qt", "pytest-cov>=2.9.0", "pytest-raises>=0.11", "quilt3~=3.4.0", From a22b1cd54118534b54e4c33ac09ffeb3077132a7 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 18:13:16 +0200 Subject: [PATCH 16/27] Test for multi-scene widget --- napari_aicsimageio/tests/test_core.py | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/napari_aicsimageio/tests/test_core.py b/napari_aicsimageio/tests/test_core.py index f6789fb..cd9c259 100644 --- a/napari_aicsimageio/tests/test_core.py +++ b/napari_aicsimageio/tests/test_core.py @@ -94,3 +94,52 @@ def test_reader( # Check meta meta.pop("metadata", None) assert meta == expected_meta # type: ignore + + +SINGLESCENE_FILE = "s_1_t_1_c_1_z_1.czi" +MULTISCENE_FILE = "s_3_t_1_c_3_z_5.czi" + + +@pytest.mark.parametrize( + "in_memory, expected_dtype", + [ + (True, np.ndarray), + (False, da.core.Array), + ], +) +@pytest.mark.parametrize( + "filename, nr_widgets, expected_shape", + [ + (SINGLESCENE_FILE, 0, (325, 475)), + (MULTISCENE_FILE, 1, (3, 5, 325, 475)), + ], +) +def test_for_multiscene_widget( + make_napari_viewer, + resources_dir: Path, + filename: str, + in_memory: bool, + nr_widgets: int, + expected_dtype: type, + expected_shape: Tuple[int, ...], +): + # Make a viewer + viewer = make_napari_viewer() + assert len(viewer.layers) == 0 + assert len(viewer.window._dock_widgets) == 0 + + # Resolve filename to filepath + if isinstance(filename, str): + path = str(resources_dir / filename) + + # Get reader + reader = core.get_reader(path, in_memory) + + # Call reader on path + reader(path) + + # Check for list widget + assert len(viewer.window._dock_widgets) == nr_widgets + + if len(viewer.window._dock_widgets) > 0: + assert list(viewer.window._dock_widgets.keys())[0] == "Scene Selector" From f9d5d6c5390358f523df4e1bf1ba1d891e93385a Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 18:20:56 +0200 Subject: [PATCH 17/27] Fix nr of widgets check --- napari_aicsimageio/tests/test_core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/napari_aicsimageio/tests/test_core.py b/napari_aicsimageio/tests/test_core.py index cd9c259..1ae414a 100644 --- a/napari_aicsimageio/tests/test_core.py +++ b/napari_aicsimageio/tests/test_core.py @@ -122,7 +122,7 @@ def test_for_multiscene_widget( nr_widgets: int, expected_dtype: type, expected_shape: Tuple[int, ...], -): +) -> None: # Make a viewer viewer = make_napari_viewer() assert len(viewer.layers) == 0 @@ -141,5 +141,5 @@ def test_for_multiscene_widget( # Check for list widget assert len(viewer.window._dock_widgets) == nr_widgets - if len(viewer.window._dock_widgets) > 0: + if len(viewer.window._dock_widgets) != 0: assert list(viewer.window._dock_widgets.keys())[0] == "Scene Selector" From 99ea94665c212b1e8e890a99e122d62de1e3fbea Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 19:49:18 +0200 Subject: [PATCH 18/27] Fix flake8 import error? --- napari_aicsimageio/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index df0fb17..ee3b662 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -5,10 +5,11 @@ from typing import Any, Callable, Dict, List, Optional, Tuple, Union import xarray as xr +import napari + +from qtpy.QtWidgets import QListWidget from aicsimageio import AICSImage, exceptions, types from aicsimageio.dimensions import DimensionNames -from qtpy.QtWidgets import QListWidget -import napari ############################################################################### From 1c21e5d104ae4b2d5652ce3760c792c4138eb1da Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 19:54:23 +0200 Subject: [PATCH 19/27] Fix flake8 imports take 2 (isort) --- napari_aicsimageio/core.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index ee3b662..8f08432 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -4,12 +4,11 @@ from functools import partial from typing import Any, Callable, Dict, List, Optional, Tuple, Union -import xarray as xr import napari - -from qtpy.QtWidgets import QListWidget +import xarray as xr from aicsimageio import AICSImage, exceptions, types from aicsimageio.dimensions import DimensionNames +from qtpy.QtWidgets import QListWidget ############################################################################### From a241491b5e36bfc4f975322169d0021519b59d10 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 19:55:17 +0200 Subject: [PATCH 20/27] AICSImageIO~4.1.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5ba6055..2eb5ee5 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ ] requirements = [ - "aicsimageio[all]~=4.0.2", + "aicsimageio[all]~=4.1.0", "fsspec[http]", # no version pin, we pull from aicsimageio "napari[all]~=0.4.11", "napari_plugin_engine~=0.1.4", From 75ae94e576170b220523275a489f1770bdf9646a Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Wed, 15 Sep 2021 23:24:32 +0200 Subject: [PATCH 21/27] Don't add a list of widgets!! --- napari_aicsimageio/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index 8f08432..1cade1c 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -51,7 +51,7 @@ def _get_scenes(img: AICSImage, in_memory: bool) -> None: for i, scene in enumerate(img.scenes): list_widget.addItem(f"{i} :: {scene}") viewer = napari.current_viewer() - viewer.window.add_dock_widget([list_widget], area="right", name="Scene Selector") + viewer.window.add_dock_widget(list_widget, area="right", name="Scene Selector") # Function to create image layer from a scene selected in the list widget def open_scene(item): From ed026a0d3161d7684022ba85e264439926a11348 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Thu, 16 Sep 2021 00:21:13 +0200 Subject: [PATCH 22/27] Test img layer from widget --- napari_aicsimageio/tests/test_core.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/napari_aicsimageio/tests/test_core.py b/napari_aicsimageio/tests/test_core.py index 1ae414a..e6223fa 100644 --- a/napari_aicsimageio/tests/test_core.py +++ b/napari_aicsimageio/tests/test_core.py @@ -110,7 +110,7 @@ def test_reader( @pytest.mark.parametrize( "filename, nr_widgets, expected_shape", [ - (SINGLESCENE_FILE, 0, (325, 475)), + (SINGLESCENE_FILE, 0, (1, 325, 475)), (MULTISCENE_FILE, 1, (3, 5, 325, 475)), ], ) @@ -143,3 +143,11 @@ def test_for_multiscene_widget( if len(viewer.window._dock_widgets) != 0: assert list(viewer.window._dock_widgets.keys())[0] == "Scene Selector" + viewer.window._dock_widgets["Scene Selector"].widget().setCurrentRow(1) + data = viewer.layers[0].data + assert isinstance(data.data, expected_dtype) + assert data.shape == expected_shape + else: + data, meta, _ = reader(path)[0] + assert isinstance(data, expected_dtype) + assert data.shape == expected_shape From e285842f77f7d38b3aac8d727478dd1f4171f740 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Thu, 16 Sep 2021 11:27:45 +0200 Subject: [PATCH 23/27] Fix (some) linting --- napari_aicsimageio/core.py | 4 ++-- napari_aicsimageio/tests/test_core.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index 1cade1c..5170f8c 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -54,7 +54,7 @@ def _get_scenes(img: AICSImage, in_memory: bool) -> None: viewer.window.add_dock_widget(list_widget, area="right", name="Scene Selector") # Function to create image layer from a scene selected in the list widget - def open_scene(item): + def open_scene(item) -> None: scene_text = item.text() # Use scene indexes to cover for duplicate names @@ -85,7 +85,7 @@ def open_scene(item): # Function to get Metadata to provide with data -def _get_meta(data, img): +def _get_meta(data, img) -> None: meta = {} if DimensionNames.Channel in data.dims: diff --git a/napari_aicsimageio/tests/test_core.py b/napari_aicsimageio/tests/test_core.py index e6223fa..c0339fa 100644 --- a/napari_aicsimageio/tests/test_core.py +++ b/napari_aicsimageio/tests/test_core.py @@ -145,9 +145,9 @@ def test_for_multiscene_widget( assert list(viewer.window._dock_widgets.keys())[0] == "Scene Selector" viewer.window._dock_widgets["Scene Selector"].widget().setCurrentRow(1) data = viewer.layers[0].data - assert isinstance(data.data, expected_dtype) - assert data.shape == expected_shape + assert isinstance(data.data, expected_dtype) # type: ignore + assert data.shape == expected_shape # type: ignore else: data, meta, _ = reader(path)[0] - assert isinstance(data, expected_dtype) - assert data.shape == expected_shape + assert isinstance(data, expected_dtype) # type: ignore + assert data.shape == expected_shape # type: ignore From 63f564d3e513cd95d72e6299b8a07ffef8ad19e7 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Thu, 16 Sep 2021 11:46:39 +0200 Subject: [PATCH 24/27] Fix linting --- napari_aicsimageio/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index 5170f8c..3ea1006 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -85,7 +85,7 @@ def open_scene(item) -> None: # Function to get Metadata to provide with data -def _get_meta(data, img) -> None: +def _get_meta(data, img) -> Dict[str, Any]: meta = {} if DimensionNames.Channel in data.dims: From ff81a0cacd2789866f3645ba0967843afd74aab9 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Thu, 16 Sep 2021 21:53:46 +0200 Subject: [PATCH 25/27] =?UTF-8?q?Linting=E2=80=94for=20real=20this=20time?= =?UTF-8?q?=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- napari_aicsimageio/core.py | 19 +++++--------- napari_aicsimageio/tests/test_core.py | 38 ++++++++++++++------------- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index 3ea1006..671c542 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -2,21 +2,14 @@ # -*- coding: utf-8 -*- from functools import partial -from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Optional import napari +from napari.types import LayerData, ReaderFunction, PathLike import xarray as xr -from aicsimageio import AICSImage, exceptions, types +from aicsimageio import AICSImage, exceptions from aicsimageio.dimensions import DimensionNames -from qtpy.QtWidgets import QListWidget - -############################################################################### - -LayerData = Union[Tuple[types.ArrayLike, Dict[str, Any], str]] -PathLike = Union[str, List[str]] -ReaderFunction = Callable[[PathLike], List[LayerData]] - -############################################################################### +from qtpy.QtWidgets import QListWidget, QListWidgetItem def _get_full_image_data(img: AICSImage, in_memory: bool) -> Optional[xr.DataArray]: @@ -54,7 +47,7 @@ def _get_scenes(img: AICSImage, in_memory: bool) -> None: viewer.window.add_dock_widget(list_widget, area="right", name="Scene Selector") # Function to create image layer from a scene selected in the list widget - def open_scene(item) -> None: + def open_scene(item: QListWidgetItem) -> None: scene_text = item.text() # Use scene indexes to cover for duplicate names @@ -85,7 +78,7 @@ def open_scene(item) -> None: # Function to get Metadata to provide with data -def _get_meta(data, img) -> Dict[str, Any]: +def _get_meta(data: xr.DataArray, img: AICSImage) -> Dict[str, Any]: meta = {} if DimensionNames.Channel in data.dims: diff --git a/napari_aicsimageio/tests/test_core.py b/napari_aicsimageio/tests/test_core.py index c0339fa..29fc9a8 100644 --- a/napari_aicsimageio/tests/test_core.py +++ b/napari_aicsimageio/tests/test_core.py @@ -2,9 +2,10 @@ # -*- coding: utf-8 -*- from pathlib import Path -from typing import Any, Dict, Tuple +from typing import Any, Callable, Dict, Tuple import dask.array as da +import napari import numpy as np import pytest @@ -115,7 +116,7 @@ def test_reader( ], ) def test_for_multiscene_widget( - make_napari_viewer, + make_napari_viewer: Callable[..., napari.Viewer], resources_dir: Path, filename: str, in_memory: bool, @@ -135,19 +136,20 @@ def test_for_multiscene_widget( # Get reader reader = core.get_reader(path, in_memory) - # Call reader on path - reader(path) - - # Check for list widget - assert len(viewer.window._dock_widgets) == nr_widgets - - if len(viewer.window._dock_widgets) != 0: - assert list(viewer.window._dock_widgets.keys())[0] == "Scene Selector" - viewer.window._dock_widgets["Scene Selector"].widget().setCurrentRow(1) - data = viewer.layers[0].data - assert isinstance(data.data, expected_dtype) # type: ignore - assert data.shape == expected_shape # type: ignore - else: - data, meta, _ = reader(path)[0] - assert isinstance(data, expected_dtype) # type: ignore - assert data.shape == expected_shape # type: ignore + if reader is not None: + # Call reader on path + reader(path) + + # Check for list widget + assert len(viewer.window._dock_widgets) == nr_widgets + + if len(viewer.window._dock_widgets) != 0: + assert list(viewer.window._dock_widgets.keys())[0] == "Scene Selector" + viewer.window._dock_widgets["Scene Selector"].widget().setCurrentRow(1) + data = viewer.layers[0].data + assert isinstance(data.data, expected_dtype) # type: ignore + assert data.shape == expected_shape # type: ignore + else: + data, meta, _ = reader(path)[0] + assert isinstance(data, expected_dtype) # type: ignore + assert data.shape == expected_shape # type: ignore From dacc2727b5b897c53705589b2fef00dce4f908fc Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Thu, 16 Sep 2021 22:00:49 +0200 Subject: [PATCH 26/27] Linting! --- napari_aicsimageio/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index 671c542..c86f926 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -5,10 +5,10 @@ from typing import Any, Dict, List, Optional import napari -from napari.types import LayerData, ReaderFunction, PathLike import xarray as xr from aicsimageio import AICSImage, exceptions from aicsimageio.dimensions import DimensionNames +from napari.types import LayerData, PathLike, ReaderFunction from qtpy.QtWidgets import QListWidget, QListWidgetItem From d194b8f1dd66970e97137077aa2f441bc2fdc133 Mon Sep 17 00:00:00 2001 From: Peter Sobolewski Date: Fri, 17 Sep 2021 19:38:25 +0200 Subject: [PATCH 27/27] Fix typing to use napari.types and TYPE_CHECKING --- napari_aicsimageio/core.py | 12 +++++++----- napari_aicsimageio/in_memory.py | 3 ++- napari_aicsimageio/out_of_memory.py | 3 ++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/napari_aicsimageio/core.py b/napari_aicsimageio/core.py index c86f926..c5d57bd 100644 --- a/napari_aicsimageio/core.py +++ b/napari_aicsimageio/core.py @@ -2,15 +2,17 @@ # -*- coding: utf-8 -*- from functools import partial -from typing import Any, Dict, List, Optional +from typing import TYPE_CHECKING, Any, Dict, List, Optional import napari import xarray as xr from aicsimageio import AICSImage, exceptions from aicsimageio.dimensions import DimensionNames -from napari.types import LayerData, PathLike, ReaderFunction from qtpy.QtWidgets import QListWidget, QListWidgetItem +if TYPE_CHECKING: + from napari.types import LayerData, PathLike, ReaderFunction + def _get_full_image_data(img: AICSImage, in_memory: bool) -> Optional[xr.DataArray]: if DimensionNames.MosaicTile in img.reader.dims.order: @@ -117,8 +119,8 @@ def _get_meta(data: xr.DataArray, img: AICSImage) -> Dict[str, Any]: def reader_function( - path: PathLike, in_memory: bool, scene_name: Optional[str] = None -) -> Optional[List[LayerData]]: + path: "PathLike", in_memory: bool, scene_name: Optional[str] = None +) -> Optional[List["LayerData"]]: """ Given a single path return a list of LayerData tuples. """ @@ -157,7 +159,7 @@ def reader_function( return [(data.data, meta, "image")] -def get_reader(path: PathLike, in_memory: bool) -> Optional[ReaderFunction]: +def get_reader(path: "PathLike", in_memory: bool) -> Optional["ReaderFunction"]: """ Given a single path or list of paths, return the appropriate aicsimageio reader. """ diff --git a/napari_aicsimageio/in_memory.py b/napari_aicsimageio/in_memory.py index e6331bd..77ca58d 100644 --- a/napari_aicsimageio/in_memory.py +++ b/napari_aicsimageio/in_memory.py @@ -3,6 +3,7 @@ from typing import Optional +from napari.types import PathLike, ReaderFunction from napari_plugin_engine import napari_hook_implementation from . import core @@ -11,5 +12,5 @@ @napari_hook_implementation -def napari_get_reader(path: core.PathLike) -> Optional[core.ReaderFunction]: +def napari_get_reader(path: PathLike) -> Optional[ReaderFunction]: return core.get_reader(path, in_memory=True) diff --git a/napari_aicsimageio/out_of_memory.py b/napari_aicsimageio/out_of_memory.py index b76efe9..4fc22fb 100644 --- a/napari_aicsimageio/out_of_memory.py +++ b/napari_aicsimageio/out_of_memory.py @@ -3,6 +3,7 @@ from typing import Optional +from napari.types import PathLike, ReaderFunction from napari_plugin_engine import napari_hook_implementation from . import core @@ -11,5 +12,5 @@ @napari_hook_implementation -def napari_get_reader(path: core.PathLike) -> Optional[core.ReaderFunction]: +def napari_get_reader(path: PathLike) -> Optional[ReaderFunction]: return core.get_reader(path, in_memory=False)