From 99861c7d454a0bcf7f3e9d22e97ab16f0bdedc36 Mon Sep 17 00:00:00 2001 From: Andreas M Arnold Date: Mon, 27 Nov 2023 17:05:06 -0500 Subject: [PATCH 01/10] Remove pts layers after Finish --- src/affinder/affinder.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/affinder/affinder.py b/src/affinder/affinder.py index d1f2f5f..f1a46a2 100644 --- a/src/affinder/affinder.py +++ b/src/affinder/affinder.py @@ -86,6 +86,13 @@ def close_affinder(layers, callback): layer.mode = 'pan_zoom' +# make a bindable function to remove points layers after finishing +@magicgui +def remove_pts_layers(viewer, layers): + for layer in layers: + viewer.layers.remove(layer) + + def _update_unique_choices(widget, choice_name): """Update the selected choice in a ComboBox widget to be unique. @@ -126,6 +133,7 @@ def _on_affinder_main_init(widget): layout='vertical', output={'mode': 'w'}, viewer={'visible': False, 'label': ' '}, + keep_pts={'label': 'Keep points layers', 'tooltip': 'Keep point layers after "Finish" '}, ) def start_affinder( viewer: 'napari.viewer.Viewer', @@ -136,6 +144,7 @@ def start_affinder( moving_points: Optional['napari.layers.Points'] = None, model: AffineTransformChoices, output: Optional[pathlib.Path] = None, + keep_pts: bool = False, ): mode = start_affinder._call_button.text # can be "Start" or "Finish" @@ -184,10 +193,15 @@ def start_affinder( close_affinder.layers.bind(points_layers) close_affinder.callback.bind(callback) + remove_pts_layers.viewer.bind(viewer) + remove_pts_layers.layers.bind(points_layers) + # change the button/mode for next run start_affinder._call_button.text = 'Finish' else: # we are in Finish mode close_affinder() + if not keep_pts: + remove_pts_layers() start_affinder._call_button.text = 'Start' From 46b1707054fc7e8c0c22996d0ca592639b288a41 Mon Sep 17 00:00:00 2001 From: Andreas M Arnold Date: Tue, 5 Dec 2023 10:48:54 -0500 Subject: [PATCH 02/10] Change keep_pts to delete_pts --- src/affinder/affinder.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/affinder/affinder.py b/src/affinder/affinder.py index f1a46a2..d5b3bdd 100644 --- a/src/affinder/affinder.py +++ b/src/affinder/affinder.py @@ -1,16 +1,17 @@ +import functools +import pathlib import warnings +from enum import Enum from typing import Optional -from enum import Enum -import pathlib +import numpy as np import toolz as tz from magicgui import magicgui, magic_factory -import numpy as np from skimage.transform import ( - AffineTransform, - EuclideanTransform, - SimilarityTransform, - ) + AffineTransform, + EuclideanTransform, + SimilarityTransform, +) class AffineTransformChoices(Enum): @@ -86,7 +87,7 @@ def close_affinder(layers, callback): layer.mode = 'pan_zoom' -# make a bindable function to remove points layers after finishing +# make function to remove points layers after finishing @magicgui def remove_pts_layers(viewer, layers): for layer in layers: @@ -133,7 +134,8 @@ def _on_affinder_main_init(widget): layout='vertical', output={'mode': 'w'}, viewer={'visible': False, 'label': ' '}, - keep_pts={'label': 'Keep points layers', 'tooltip': 'Keep point layers after "Finish" '}, + delete_pts={'label': 'Delete points layers when done ', + 'tooltip': 'If ticked, the points layers used in alignment will be deleted when clicking "Finish".'}, ) def start_affinder( viewer: 'napari.viewer.Viewer', @@ -144,7 +146,7 @@ def start_affinder( moving_points: Optional['napari.layers.Points'] = None, model: AffineTransformChoices, output: Optional[pathlib.Path] = None, - keep_pts: bool = False, + delete_pts: bool = False, ): mode = start_affinder._call_button.text # can be "Start" or "Finish" @@ -200,7 +202,7 @@ def start_affinder( start_affinder._call_button.text = 'Finish' else: # we are in Finish mode close_affinder() - if not keep_pts: + if delete_pts: remove_pts_layers() start_affinder._call_button.text = 'Start' From 24aef9215f95200eca78a283fd3c7e950e2f6c3b Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 11 Dec 2023 16:05:50 +1100 Subject: [PATCH 03/10] yapf --- src/affinder/affinder.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/affinder/affinder.py b/src/affinder/affinder.py index d5b3bdd..10d8a54 100644 --- a/src/affinder/affinder.py +++ b/src/affinder/affinder.py @@ -8,10 +8,10 @@ import toolz as tz from magicgui import magicgui, magic_factory from skimage.transform import ( - AffineTransform, - EuclideanTransform, - SimilarityTransform, -) + AffineTransform, + EuclideanTransform, + SimilarityTransform, + ) class AffineTransformChoices(Enum): From 8eaca5436cd60c5bbd0ff0b827f8e890d313465f Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 11 Dec 2023 16:08:25 +1100 Subject: [PATCH 04/10] Use on-instance references instead of bindable magicgui instance --- src/affinder/affinder.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/affinder/affinder.py b/src/affinder/affinder.py index 10d8a54..4d4ebb2 100644 --- a/src/affinder/affinder.py +++ b/src/affinder/affinder.py @@ -6,7 +6,7 @@ import numpy as np import toolz as tz -from magicgui import magicgui, magic_factory +from magicgui import magic_factory from skimage.transform import ( AffineTransform, EuclideanTransform, @@ -79,8 +79,6 @@ def next_layer_callback( reset_view(viewer, reference_image_layer) -# make a bindable function to shut things down -@magicgui def close_affinder(layers, callback): for layer in layers: layer.events.data.disconnect(callback) @@ -88,7 +86,6 @@ def close_affinder(layers, callback): # make function to remove points layers after finishing -@magicgui def remove_pts_layers(viewer, layers): for layer in layers: viewer.layers.remove(layer) @@ -134,8 +131,12 @@ def _on_affinder_main_init(widget): layout='vertical', output={'mode': 'w'}, viewer={'visible': False, 'label': ' '}, - delete_pts={'label': 'Delete points layers when done ', - 'tooltip': 'If ticked, the points layers used in alignment will be deleted when clicking "Finish".'}, + delete_pts={ + 'label': + 'Delete points layers when done ', + 'tooltip': + 'If ticked, the points layers used in alignment will be deleted when clicking "Finish".' + }, ) def start_affinder( viewer: 'napari.viewer.Viewer', @@ -192,18 +193,18 @@ def start_affinder( viewer.layers.selection.active = pts_layer0 pts_layer0.mode = 'add' - close_affinder.layers.bind(points_layers) - close_affinder.callback.bind(callback) - - remove_pts_layers.viewer.bind(viewer) - remove_pts_layers.layers.bind(points_layers) - + start_affinder.close = functools.partial( + close_affinder, points_layers, callback + ) + start_affinder.remove_points_layers = functools.partial( + remove_pts_layers, viewer, points_layers + ) # change the button/mode for next run start_affinder._call_button.text = 'Finish' else: # we are in Finish mode - close_affinder() + start_affinder.close() if delete_pts: - remove_pts_layers() + start_affinder.remove_points_layers() start_affinder._call_button.text = 'Start' From 39109e5b9ab66918eeac4b103078881113ad178d Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 11 Dec 2023 16:39:02 +1100 Subject: [PATCH 05/10] Update delete_pts messages --- src/affinder/affinder.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/affinder/affinder.py b/src/affinder/affinder.py index 4d4ebb2..798e07a 100644 --- a/src/affinder/affinder.py +++ b/src/affinder/affinder.py @@ -133,9 +133,11 @@ def _on_affinder_main_init(widget): viewer={'visible': False, 'label': ' '}, delete_pts={ 'label': - 'Delete points layers when done ', - 'tooltip': - 'If ticked, the points layers used in alignment will be deleted when clicking "Finish".' + 'Delete points layers when done', + 'tooltip': ( + 'If ticked, the points layers used in alignment ' + 'will be deleted when clicking "Finish".' + ), }, ) def start_affinder( From 004fc00fef795c410c25ecb9cba307832b5eb490 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 11 Dec 2023 20:16:07 +1100 Subject: [PATCH 06/10] Add test for delete_pts --- src/affinder/_tests/test_affinder.py | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/affinder/_tests/test_affinder.py b/src/affinder/_tests/test_affinder.py index bd7ab72..6e0acc6 100644 --- a/src/affinder/_tests/test_affinder.py +++ b/src/affinder/_tests/test_affinder.py @@ -165,3 +165,33 @@ def test_load_affine(tmp_path): widget(layer, affile) np.testing.assert_allclose(layer.affine, affine) + + +@pytest.mark.parametrize('remove_pts', [True, False]) +def test_remove_points_layers(remove_pts, make_napari_viewer): + """Check whether remove_points_layer option actually removes the layers.""" + ref_im = np.random.random((5, 5)) + mov_im = np.random.random((5, 5)) + ref_pts = np.array([[1, 1], [2, 2], [1, 4]], dtype=float) + mov_pts = np.array([[4, 1], [2, 2], [1, 4]], dtype=float) + + viewer = make_napari_viewer() + qtwidget, widget = viewer.window.add_plugin_dock_widget( + 'affinder', 'Start affinder' + ) + widget( + viewer=viewer, + reference=ref_im, + moving=mov_im, + model=AffineTransformChoices.affine, + delete_pts=remove_pts + ) + viewer.layers['ref_im_pts'].data = ref_pts + viewer.layers['mov_im_pts'].data = mov_pts + + widget() # close the widget + + assert remove_pts != any( + pt_layer in viewer.layers + for pt_layer in ['ref_im_pts', 'mov_im_pts'] + ) From 2936eb32e3418c8cd67bb6aea9c8f9a18e91c5e4 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 11 Dec 2023 20:24:42 +1100 Subject: [PATCH 07/10] Add image before starting affinder --- src/affinder/_tests/test_affinder.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/affinder/_tests/test_affinder.py b/src/affinder/_tests/test_affinder.py index 6e0acc6..45c233a 100644 --- a/src/affinder/_tests/test_affinder.py +++ b/src/affinder/_tests/test_affinder.py @@ -176,6 +176,8 @@ def test_remove_points_layers(remove_pts, make_napari_viewer): mov_pts = np.array([[4, 1], [2, 2], [1, 4]], dtype=float) viewer = make_napari_viewer() + viewer.add_image(ref_im) + viewer.add_image(mov_im) qtwidget, widget = viewer.window.add_plugin_dock_widget( 'affinder', 'Start affinder' ) From 3d34b7f33b62a715e9eddd6fac1322856049f4d2 Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 11 Dec 2023 20:46:46 +1100 Subject: [PATCH 08/10] Use layer, not numpy as input to affinder --- src/affinder/_tests/test_affinder.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/affinder/_tests/test_affinder.py b/src/affinder/_tests/test_affinder.py index 45c233a..6059051 100644 --- a/src/affinder/_tests/test_affinder.py +++ b/src/affinder/_tests/test_affinder.py @@ -176,15 +176,15 @@ def test_remove_points_layers(remove_pts, make_napari_viewer): mov_pts = np.array([[4, 1], [2, 2], [1, 4]], dtype=float) viewer = make_napari_viewer() - viewer.add_image(ref_im) - viewer.add_image(mov_im) + ref_layer = viewer.add_image(ref_im) + mov_layer = viewer.add_image(mov_im) qtwidget, widget = viewer.window.add_plugin_dock_widget( 'affinder', 'Start affinder' ) widget( viewer=viewer, - reference=ref_im, - moving=mov_im, + reference=ref_layer, + moving=mov_layer, model=AffineTransformChoices.affine, delete_pts=remove_pts ) From 69f800468cd6a01e2e5f65863a70ea8f95b03e9e Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Mon, 11 Dec 2023 20:54:24 +1100 Subject: [PATCH 09/10] Make sure that calling the widget actually worked --- src/affinder/_tests/test_affinder.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/affinder/_tests/test_affinder.py b/src/affinder/_tests/test_affinder.py index 6059051..a7b077f 100644 --- a/src/affinder/_tests/test_affinder.py +++ b/src/affinder/_tests/test_affinder.py @@ -193,6 +193,8 @@ def test_remove_points_layers(remove_pts, make_napari_viewer): widget() # close the widget + assert widget._call_button.text == 'Start' + assert remove_pts != any( pt_layer in viewer.layers for pt_layer in ['ref_im_pts', 'mov_im_pts'] From 4156cb1fe7dde94560fa4b2b505b7d62a3c0625c Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Tue, 12 Dec 2023 16:40:47 +1100 Subject: [PATCH 10/10] What matters is whether delete_pts is selected on close --- src/affinder/_tests/test_affinder.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/affinder/_tests/test_affinder.py b/src/affinder/_tests/test_affinder.py index a7b077f..57fd158 100644 --- a/src/affinder/_tests/test_affinder.py +++ b/src/affinder/_tests/test_affinder.py @@ -186,12 +186,11 @@ def test_remove_points_layers(remove_pts, make_napari_viewer): reference=ref_layer, moving=mov_layer, model=AffineTransformChoices.affine, - delete_pts=remove_pts ) viewer.layers['ref_im_pts'].data = ref_pts viewer.layers['mov_im_pts'].data = mov_pts - widget() # close the widget + widget(delete_pts=remove_pts) # close the widget assert widget._call_button.text == 'Start'