-
Notifications
You must be signed in to change notification settings - Fork 39
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
how to use annotation with create_widget #68
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @gatoniel - welcome and thanks for your PR! I left one minor comment (to sctually create a link to the magicgui-and-type-annotations
section) but will leave the actual example to be reviewed by one of the core maintainers. Cheers!
This is a much more generous mechanism. So getting layer selector should be mentioned as an example of usage, not primary usage. Maybe it could also be nice to add an example using |
Hi folks - I'd love to see this one go in. I will link to it in zulip to maybe get more eyes on it 😄 |
Co-authored-by: Melissa Weber Mendonça <melissawm@gmail.com>
Hi @gatoniel - do you need help with updating your branch, or moving this forward? Thanks! |
@lucyleeow @DragaDoncila |
This looks useful but I think it is missing some context. In what situations would you not be able to use the I think this would be a better example than what is used here: https://napari.org/dev/howtos/extending/magicgui.html#qtwidgets-qwidget and we can over-write it. Could we add some more detail about how @gatoniel are you still interested in continuing this PR? |
Gentle ping, @gatoniel ! Let us know if you need help 😄 |
Reading again, I think it would be a good addition to the existing example. We could explain why/how |
Hi, sorry for the late reply. I was / am busy with other stuff. Maybe I will have time this weekend. |
I do not know whether this question requires an answer as long as the information is available at https://napari.org/dev/howtos/extending/magicgui.html#qtwidgets-qwidget. For now, I have added the example as a special case there as people using
Unfortunately, I do not know enough about the other possibilities of
However, in the example under https://napari.org/dev/howtos/extending/magicgui.html#magicgui-widgets-container |
I think there is very useful and important information here (e.g., regarding refreshing the widget) that is not documented anywhere else. Fitting it in with existing documentation is more tricky and probably more work than originally intended. How about I try and add this example, along with some more info, and you can review if you are interested @gatoniel ? |
Sounds good. Otherwise, I could also add some more description about the refreshing into the surrounding text. |
I have now added some more description about the refreshing and a note box in the #parameter-annotations section to direct the reader directly to this example if she is not concerned about |
Ah that is interesting. @DragaDoncila and I were talking about whether the |
Now that I understand better, I think we could just have one I think some more details about what is happening in the 2 methods would be useful too. And highlight that we need to update ourselves as magicgui is not doing this for us. There are also several improvements I would like to make to that page, but this can be separate to this addition. Overall, maybe this is too much extra work for you? |
docs/howtos/extending/magicgui.md
Outdated
|
||
You might want to add a layer selection as [shown above](#parameter-annotations) into your highly customizable `QtWidgets.QWidget` that is always synchronized with the available {attr}`napari.types.ImageData` layers in your viewer. For this you can use the {func}`create_widget <magicgui.widgets.create_widget>` function as described in the following example. | ||
|
||
To synchronize the information between the napari viewer (i.e. the available layers) the function `reset_choices` of the dropdown widget needs to be manually called whenever the main widget is shown. In the following example, this is done by defining two class methods `showEvent` and `reset_choices`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have thought it needs to be called when the input selector widget is shown? If the main widget is open. then layers are changed, you'd expect the input selector widget to update?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested it. Napari somehow internally calls reset_choices
of the main widget. So showEvent
and reset_choices
need to be named exactly like that. I updated it in the PR
Yes, technically it doesn't matter what this method is called. One could also call the
Depends. If I get rather precise orders, I can try :D |
This was wrong. I tested the available options. It works only smoothly, when the function is exactly called |
Yeah I found this also but I couldn't for the life of me figure out where the connection is made. I can see that for magicgui widgets we have a signal What I don't understand is how having a method named Pinging @tlambert03 who can hopefully unravel some of the magic in magicgui. |
a grep of napari for reset_choices suggests its probably this: (i.e. it's not magicgui magic, it's napari magic... i'm not sure if you want to "codify" that as a first class API here, but that's why it's working 😄 ) |
@gatoniel @lucyleeow I would say to move this PR forward while we think about how/if we want to maintain this |
@gatoniel thanks for uncovering all of this, sorry this PR has gotten a bit derailed. |
I changed the example. Now the |
Thanks @gatoniel. I still think that we only need one Could you also split your lines so max line length is 79 characters (this is what we've set for napari (see: here) but the docs repo has no linting). Thank you |
docs/howtos/extending/magicgui.md
Outdated
```{note} | ||
If you're interested in getting synchronized information from the napari viewer into widgets without `@magicgui`, see the [`create_widget` example](#magicgui-widgets-create_widget) below. | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is necessary. I think we automatically update for all magicgui registered napari types; layer, layer subclasses and layer type data (e.g., napari.types.ImageData
).
I am not sure if there would be any cases where you would want an input that is not in the above list.
If there is, we could mention that for non registered types you need to manually synchronize, see QWidget
example for more...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have made the note more precise. I added it, because I was aware that one can easily create these synchronized dropdown lists of the available layers with @magicgui
. But then, they were part of a bigger widget, and I was not able to figure out to create only these widgets which is possible with create_widget
.
Done. Is it ok now?
It is only one widget now. Clicking on the button prints the shape of the currently selected Image Layer. I mentioned that it needs manual connection to the layers events. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, just some nits, thanks!
Co-authored-by: Lucy Liu <jliu176@gmail.com>
Co-authored-by: Lucy Liu <jliu176@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your work and patience on this @gatoniel! I suggested a more explicit comment about why we have to manually connect to reset_choices
now, but am approving otherwise. Let's get this in!
Co-authored-by: Draga Doncila Pop <17995243+DragaDoncila@users.noreply.github.com>
Co-authored-by: Draga Doncila Pop <17995243+DragaDoncila@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a widget has reset_choices
method, then it will be called on all required events.
Fell free to reformat my text.
manually connect the `reset_choices` of the created widget with the | ||
`viewer.layers.events` so that the available choices are synchronized | ||
with the current layers of the viewer: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
manually connect the `reset_choices` of the created widget with the | |
`viewer.layers.events` so that the available choices are synchronized | |
with the current layers of the viewer: | |
define `reset_choices` method that will need to call `reset_choices` | |
of magicgui widgets. |
"Selected layer has shape: ", | ||
self.layer_select.value.shape, | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def reset_choices(self): | |
self.layer_select.reset_choices() |
layers_events = self.viewer.layers.events | ||
layers_events.inserted.connect(self.layer_select.reset_choices) | ||
layers_events.removed.connect(self.layer_select.reset_choices) | ||
layers_events.reordered.connect(self.layer_select.reset_choices) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
layers_events = self.viewer.layers.events | |
layers_events.inserted.connect(self.layer_select.reset_choices) | |
layers_events.removed.connect(self.layer_select.reset_choices) | |
layers_events.reordered.connect(self.layer_select.reset_choices) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, we can explain that part of napari magic, too, and show both possibilities with a marker that one of them is potentially deprecated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are working on nested Layers (Layer Groups). It may change a list of events. So then people will need to update their plugins if they do not depend on napari calling reset_choices
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So then people will need to update their plugins if they do not depend on napari calling reset_choices.
@Czaki I'm not happy to enshrine this behaviour in our docs right now (see original comment here). It's very brittle and in my opinion, quite an overreach that could lead to surprising effects on widgets totally unrelated to the layerlist. I would strongly prefer to keep the example as it currently is, because it involves no magic and no reliance on napari
internals. If we one day change the layerlist
events then yes, people may have to update their plugins, but I have no issue with that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we explain the shortcut via reset_choices
in the docs and also mention that there are plans that both viewer.layers.events
and reset_choices
might change in future, so that developers should check the corresponding source code once there plugins stop working as expected.
reset_choices
https://github.com/napari/napari/blob/50b314f7ad2e7cd4ef21b5705b3c6b363c0857ed/napari/_qt/qt_main_window.py#L1093 shows all the relevant layers.events
that would need to be connected individually.
viewer.layers.events
currently is a instance subclassing https://github.com/napari/napari/blob/50b314f7ad2e7cd4ef21b5705b3c6b363c0857ed/napari/utils/events/containers/_selectable_list.py#L12
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the offer @gatoniel —if we decide to take you up on it or parts of it, I think expanding on the reset_choices magic should come in a separate PR. For now I think we are still internally deciding whether we want it in the docs, as you can see 😅, so I think this should go in as-is.
Regarding the future of the event connections: in my opinion, if we start worrying in the docs about all the things we are planning to deprecate, we will never document anything. 😂 I think in general the docs should be written with regard to the current public API, unless we really want to discourage use of a specific API. Which is not the case here.
Hi folks, I'm with Draga and Lucy here — I find the napari magic um, concerning, 😂, insomuch as it introduces a way-too-tight coupling between napari and magicgui, so I would rather we use the explicit connections in the docs. Yes @Czaki, the events might change, but they are part of the public API so in that case we would use a deprecation warningemitter cycle and so on and we would be able to update. (Of course, keeping the docs in sync is still a challenge, but that is the case either way — we might also remove the totally-private reset_choices magic and then the example would stop working — with no warning.) @gatoniel thanks for offering to document both options — that is appreciated, but even if we decide to go that route, in my opinion the example as it is now is good and should go in, and we can expand on the magic in a subsequent PR. I'll mark this as ready-to-merge and if @Czaki agrees with my rationale above, he can merge. 🙏 🚀 Thanks all for the discussion! I had actually not seen this page @lucyleeow, it is friggin GOLD. 🥇 |
I still do not understand why you call this magic. |
it is magical to connect events behind the scenes based on a method name. (Not the signature, nor what the method does.) This is especially true if the interface is not annotated anywhere, specifically in the function that is adding the widget. |
At any rate @Czaki I don't think you can argue that layer events are public and therefore can be considered at least as stable as this internal connection, whether or not you call it magic? And that the docs are improved by this PR. I'd be happy to move this discussion to a new PR that discusses both options and their tradeoffs, as proposed by @gatoniel. |
# References and relevant issues Came about after #68 Details `magicgui` type registration, in particular the `reset_choices` connection to layers we do for all widgets that have that method.
# References and relevant issues Follow on from #68 # Description * adds more info on when you would want to choose each type of widget, clarifying that widgets with access to the native QWidget are equally extensible. * clarify you can use `create_widget` in any widget class * Move the "Avoid imports with forward references" info to plugin best practices, where we already have a section about avoiding unnecessary imports * Move '`magicgui` function widgets as plugin contributions' to be next to the intro to magicgui function widgets
Description
Adds to the documentation how to use annotations with
magicgui.widgets.create_widget
in a plugin subclass ofQtWidgets.QWidget
.Type of change
References
closes napari/napari#5340
uses information from https://github.com/PartSeg/PartSeg_bioimageio/blob/2afcbd558e52d6af6a13f0b844e80d280cd6ba9d/src/PartSeg_bioimageio/_widget.py#L421
Final checklist: