Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider/brainstorm about a "Unique" type modifier for inputs #561

Open
jni opened this issue May 29, 2023 · 2 comments
Open

Consider/brainstorm about a "Unique" type modifier for inputs #561

jni opened this issue May 29, 2023 · 2 comments

Comments

@jni
Copy link
Contributor

jni commented May 29, 2023

In some situations, values in an API are of the same type but can't have the same value. Particularly, any time you have a function to compare or match two different objects.

In this case, you want the same widget for the two values in the generated UI but you want to prevent the same value from being selected. The motivating example is choosing images for registration in affinder, but there's certainly others, such as choosing two segmentations (e.g. automatic and ground truth) to compute an accuracy score, or various kinds of "image math".

This can be done in user space as suggested by @tlambert03 (example at jni/affinder#63), but it seems useful enough to support more generally. I don't really know how I would go about it though so I'm hoping you can write a sketch of it here @tlambert03? 🙏

@tlambert03
Copy link
Member

the thing that makes this a bit tricky in my mind (and which makes me slightly hesitant to offer it as a feature) is the problem of the "scope" of the uniqueness. For example, in affinder, the goal is simple enough if you consider a single affinder widget (which has two comboboxes) in a single napari viewer... but what if you dropped in another affinder widget? i would assume those options only need to be unique within a given affinder widget instance, within a single viewer.

Offering unique options means having a set of all possible options, and then narrowing based on some awareness of the other widgets that are also monitoring that set of options... and that's the bit that seems like it would have a number of very valid permutations, and which i'd rather not be in the business of managing.

here's a very rough, currently broken sketch:

from typing import NewType

from magicgui import magicgui, register_type

import napari
import napari.layers
from napari.utils._magicgui import find_viewer_ancestor


def get_unique_shapes(gui):
    if not gui.parent or (viewer := find_viewer_ancestor(gui)) is None:
        return []

    # get all available layer options
    all_shapes = (
        layer
        for layer in viewer.layers
        if isinstance(layer, napari.layers.Shapes)
    )

    # get values claimed by other widgets
    # this is an ugly hack to figure out the other magicgui widgets in the same widget
    # it uses both the Qt and magicgui APIs... look into a better way
    other_values = {
        w._magic_widget.value
        for w in gui.parent.parent().children()
        if hasattr(w, '_magic_widget')
        and w._magic_widget is not gui
    }

    # return all options that are not claimed by other widgets
    return [layer for layer in all_shapes if layer not in other_values]


UniqueShapes = NewType('UniqueShapes', napari.layers.Shapes)
register_type(UniqueShapes, choices=get_unique_shapes)


@magicgui
def widget(layer1: UniqueShapes, layer2: UniqueShapes):
    ...


viewer = napari.Viewer()
viewer.window.add_dock_widget(widget)
viewer.layers.events.inserted.connect(widget.reset_choices)
viewer.layers.events.removed.connect(widget.reset_choices)
# need something like this too, but it's causing recursion errors at the moment
widget.layer1.changed.connect(widget.layer2.reset_choices)
widget.layer2.changed.connect(widget.layer1.reset_choices)
napari.run()

I say currently broken because there's a still a recursion error when you try to update the options in the other widgets whenever one of the widgets changes its value.

Anyway, that gives you a sense for how it could be done, and also how many tricky gotchas there might be in a successful implementation

@imagesc-bot
Copy link

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/napari-magic-factory-image-inputs/97225/2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants