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

NAP-6: Updates after discussion and review #312

Merged
merged 27 commits into from
Jun 22, 2024
Merged

Conversation

DragaDoncila
Copy link
Contributor

@DragaDoncila DragaDoncila commented Jan 3, 2024

Description

This PR updates NAP-6 after feedback on #77, discussion at the napari hackathon (notes here but also linked in doc), and discussions in community meetings in recent weeks.

Still to be decided

  • npe2 allows users to define new submenus as well as menu items. Should plugin developers be allowed to add new submenus to contributable menus? Decision: yes, but not to the top level Layers menu, and the context menu shouldn't actually be contributable as it's already very long.
  • It's not clear that the Tracks and Classification submenus are as well defined as the rest. Classification at least I'm not even sure how to describe. Should we remove Tracks and Classification from the initial list of contributable menus? Decision: No, we like both!

@github-actions github-actions bot added the documentation Improvements or additions to documentation label Jan 3, 2024
@DragaDoncila DragaDoncila added this to the 0.5.0 milestone Jan 3, 2024
Copy link
Collaborator

@lucyleeow lucyleeow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flyby review. Will have a better look later!

docs/naps/6-contributable-menus.md Outdated Show resolved Hide resolved
docs/naps/6-contributable-menus.md Show resolved Hide resolved
docs/naps/6-contributable-menus.md Outdated Show resolved Hide resolved
Comment on lines 421 to 422
to a much greater extent. This should include contribution-level enablement control
for plugins, as well as building their own custom menus, whether this be a brand new top-level
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naive question, how would users control plugin contribution enablement..?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine, a tree selection widget like this one:

flutter tree view

I think there should be some internal npe2 data structure for this, but the UI itself should itself be a plugin. (Maybe built-ins.)

Copy link
Collaborator

@lucyleeow lucyleeow May 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be separate to our plugin manager (i.e. the install/uninstall/enable/disable dialog)?

(plugin manager has several meanings in napari: #321 (comment) 🙈 )

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ambivalent about it, and would leave that decision to whoever ends up implementing it.

On the one hand, it can be an independent dialog — enablement of contributions only needs npe2 and stuff already installed, and doesn't need to depend on conda, pip, etc. So you eliminate a lot of complexity. Additionally, I personally would never use the plugin manager (I directly install things with micromamba as needed), but I would probably use the enablement UI because the alternative, I think, is editing some random yaml or something? 😂 Which I'm not super excited about.

On the other hand, I can imagine a user being in the enablement UI and thinking hmm, I really don't use this plugin, I should remove it. And they would probably be grateful if that can be done in the same widget rather than have to launch a totally different interface.

docs/naps/6-contributable-menus.md Outdated Show resolved Hide resolved
docs/naps/6-contributable-menus.md Show resolved Hide resolved
@jni
Copy link
Member

jni commented May 28, 2024

Ok so I finally read NAP-6. Better late than never???? 😅😅😅😅😅 Thank you @DragaDoncila for your patience with me. 😅

And thanks for writing the original NAP and for this update! Here are my current comments:

  • I think eventually we will have an Edit top level menu that would include copy, paste, delete, undo, redo, select all, deselect all. Is that confusing with Layer > Edit? (ie should we rename the layer > edit submenu? or should we remove it and put those items in Edit? or put certain items in both?)

  • I found this bit super icky:

    menus:
      napari/layers/context:
        - submenu: context_submenu
        - command: napari-demo.menu_item
      context_submenu:
        - command: napari-demo.submenu_item
    
    submenus:
      - id: context_submenu
        label: A new submenu
    

    Do we really need a submenus section? I suggest that a menu is a menu is a menu, and it can be nested in another menu, and the only thing we don't allow is not nesting ancestors (ie no cycles).

  • For each menu or submenu proposed, there should be at least one concrete example. I had to think quite a bit about what would go in the "visualization" menu. Auto-contrast-limits? Toggle visibility?

  • This is the grammar nazi in me but we need to be consistent with word type/conjugation. It was jarring to have "Segmentation" followed by "Tracks". I have a mild preference for present imperative verbs: Acquire; Filter; Segment; Track; Measure. Not least because they are significantly shorter than "-ation" words.

  • File > I/O and Acquire (using new nomenclature 😂) are quite related. I wonder whether we should have an I/O top level menu under which Acquire, Open Dialog, and Export would be menus/submenus. But I see the tension with the File menu...

  • In a similar vein, things like the midi controllers, stream decks, space mouses, etc, would all live in something like "Controllers", "Devices", or "Hardware", the last of which could naturally group with "Acquire". Just a thought, since moving/removing is hard compared to adding, so an Acquisition top-level menu is a Big Deal.

  • (I super love that the NAP documents the process for updating the list of contributable menus though ❤️‍🔥)

  • in the "user configuration" section, there's a bunch of proposals that I think don't need to happen immediately — I suggest making it clear that this is for future work.

  • there's a typo "suers" instead of "users". Hopefully not an omen. 😜

  • Finally, the discussion section needs to be a bit more thorough in providing a synthesis of past discussion, including objections to the current proposal and the outcome to those objections. Even if the outcome is "brushed aside as too onerous" 😂 But reading "there is much discussion" is jarring. 😂

That's it from me! It's not a tiny list but I think this is very close. ❤️ Thanks!

@lucyleeow
Copy link
Collaborator

lucyleeow commented May 29, 2024

I found this bit super icky:

I think this ties in with the wider discussion about what the manifest should look like: napari/napari#6227

With submenus in particular, you either have to give the full path (i.e. parent menu) every time you use it, or you have a submenu section that defines submenu and its parent, and then use the shorter name every time you use the submenu.

With an updated manifest, having a submenu section may not look as bad?

@DragaDoncila
Copy link
Contributor Author

Thanks @lucyleeow and @jni I've made some updates would appreciate another pass. Also @jni some responses inline.

  • I think eventually we will have an Edit top level menu that would include copy, paste, delete, undo, redo, select all, deselect all. Is that confusing with Layer > Edit? (ie should we rename the layer > edit submenu? or should we remove it and put those items in Edit? or put certain items in both?)

Yep you're not wrong, @psobolewskiPhD has brought this up too. I don't know what exactly the distinction should be there. I would look to other tools - I know Photoshop also has Edit and Layer so that's probably a good place to start...? @isabela-pf you might have insights here specifically.

Do we really need a submenus section? I suggest that a menu is a menu is a menu, and it can be nested in another menu, and the only thing we don't allow is not nesting ancestors (ie no cycles).

Yeah I don't love it.. in a world where commands have associated menu items, then the submenus thing would just have the label and there'd be no menus section, so maybe wouldn't be as bad, or you could nest that too. I have kept the NAP so far in its current state, but if you think it's worth pushing a manifest change before 0.5.0, we can update the NAP. Otherwise we can just follow up to the ugliness with an auto-convert for the updated manifest?

  • For each menu or submenu proposed, there should be at least one concrete example. I had to think quite a bit about what would go in the "visualization" menu. Auto-contrast-limits? Toggle visibility?

I've added a couple more examples. You're welcome to suggest your own 😛 Otherwise definitely let me know which ones you can't think of an example for. W.r.t the visualization one specifically, Lorenzo and I did have this discussion.

  • This is the grammar nazi in me but we need to be consistent with word type/conjugation. It was jarring to have "Segmentation" followed by "Tracks". I have a mild preference for present imperative verbs: Acquire; Filter; Segment; Track; Measure. Not least because they are significantly shorter than "-ation" words.

Fixed, I like the shorter imperatives as well. I think I had it like that at one stage but then changed it...

  • File > I/O and Acquire (using new nomenclature 😂) are quite related. I wonder whether we should have an I/O top level menu under which Acquire, Open Dialog, and Export would be menus/submenus. But I see the tension with the File menu...
  • In a similar vein, things like the midi controllers, stream decks, space mouses, etc, would all live in something like "Controllers", "Devices", or "Hardware", the last of which could naturally group with "Acquire". Just a thought, since moving/removing is hard compared to adding, so an Acquisition top-level menu is a Big Deal.

I think I/O at top level is pretty strange compared to File -> I/O.... But yes I agree top-level is a big deal. We could do File -> Acquire which is honestly not bad...?

  • in the "user configuration" section, there's a bunch of proposals that I think don't need to happen immediately — I suggest making it clear that this is for future work.

Added a sentence!

  • there's a typo "suers" instead of "users". Hopefully not an omen. 😜

🙈

  • Finally, the discussion section needs to be a bit more thorough in providing a synthesis of past discussion, including objections to the current proposal and the outcome to those objections. Even if the outcome is "brushed aside as too onerous" 😂 But reading "there is much discussion" is jarring. 😂

Boo 😜 Still need to do this and update the TODOs.

@jni
Copy link
Member

jni commented Jun 5, 2024

I know Photoshop also has Edit and Layer so that's probably a good place to start...?

But Photoshop doesn't seem to have a Layer > Edit menu. (screenshot from the web.)

Reading the example text, "The items in this submenu allow you to change the data of your layer", I would consider renaming this to Layer > Modify, or Layer > In-Place > ...

I would also say "to change the data and metadata of your layer".

Finally, I'm quite apprehensive about supporting/encouraging in-place filters at all. I might suggest that we should remove filters from the example and focus instead on metadata...

if you think it's worth pushing a manifest change before 0.5.0, we can update the NAP. Otherwise we can just follow up to the ugliness with an auto-convert for the updated manifest?

My gut feeling is that it is worth updating the schema. I'm not 100% sure about Lucy's idea that this would imply always typing in the full menu path. I think it's possible to say that if the tail of the ID is unique, you can just use that ID; if there is ambiguity, you must use the full path. So, you'd have to write "layer/edit", but "segment" would automatically point to with "layer/segment". And also the parent path would be implied in the submenu: entry (maybe rename to "contains:"?), so you would have:

menus:
  layers:
    - contains: edit
  layers/edit:
    - command: copy_transformations

Honestly, I can easily imagine that this approach could devolve into a mess, but my gut feeling is that it's worth trying it out.

You're welcome to suggest your own 😛

An example would be a widget that allows you to select which subset of points is visible based on feature properties

I know we don't have auto-actions/purely functional inputs, but how far are those? Can we use those examples in the NAP? I would like something like "Another example would be a function that automatically sets the contrast limits based on properties of the data". And toggle visibility surely would be in this menu?

I think I/O at top level is pretty strange compared to File -> I/O.... But yes I agree top-level is a big deal. We could do File -> Acquire which is honestly not bad...?

I'm trying to say that "Acquire" is a subset of "I/O". So maybe File > Acquire, File > Load?

Anyway, a side benefit of my menus proposal above is that people can just contribute to "acquire" and we can actually move acquire around and everything would Just Work!


Getting there!

@lucyleeow
Copy link
Collaborator

My gut feeling is that it is worth updating the schema.

Are you talking about over hauling the manifest template and addressing napari/napari#6227 ?

I'm not 100% sure about Lucy's idea that this would imply always typing in the full menu path

My bad, I was already thinking in terms of a 'new' manifest schema where for each command you list all its 'contribution' types. At least this is format we think we want the manifest data to be represented as, internally in napari, from discussions with @DragaDoncila. Something like:

Dict[command-id, dict[ContributionType,  List[ContributionType]

@DragaDoncila
Copy link
Contributor Author

But Photoshop doesn't seem to have a Layer > Edit menu. (screenshot from the web.)

Sure yeah, I guess we need to take a closer look at what exactly the separation is that they've made, and see where it makes sense for us, and maybe add the Edit menu at the top level now with all the stuff we think should be in it.

Finally, I'm quite apprehensive about supporting/encouraging in-place filters at all. I might suggest that we should remove filters from the example and focus instead on metadata...

I think people will absolutely want to be editing their data and their metadata potentially so idk, unless you want to make some blanket rule like "every action returns a new layer" I'd rather have a dedicated spot for that stuff to go...

My gut feeling is that it is worth updating the schema.

I'm open to that with the caveat that it will delay things by potentially some weeks. I've uploaded a tentative new manifest here for us to have a look at and make sure we're talking about the same thing. In this one, as @lucyleeow has mentioned, the menus are attached directly to their commands, but we keep a submenu section where we declare the labels for the submenus. We still determine the root of the submenu from its prefix. What do you think about this?

I know we don't have auto-actions/purely functional inputs, but how far are those?

You know nothing, Jon Snow. Functional inputs are totally possible, and if they return layer type things those get added to the viewer, and if they request layer type things those get provided afaik. The File -> New Layer menu should contain a few pure commands I imagine.

Can we use those examples in the NAP? I would like something like "Another example would be a function that automatically sets the contrast limits based on properties of the data".

I've added that example, and I've expanded the section about 'what do menu contributions do' to talk about them.

And toggle visibility surely would be in this menu?

Unless it goes in top level edit?

I'm trying to say that "Acquire" is a subset of "I/O". So maybe File > Acquire, File > Load?

Let's go with File > Acquire. Updated.

Anyway, a side benefit of my menus proposal above is that people can just contribute to "acquire" and we can actually move acquire around and everything would Just Work!

This I love, and I've kept it in the proposed manifest above.

@DragaDoncila
Copy link
Contributor Author

Anyway, a side benefit of my menus proposal above is that people can just contribute to "acquire" and we can actually move acquire around and everything would Just Work!

Just realized it's going to be messy having the keys be just the menu name with absolutely no prefix, because of potential clashes with other plugin submenus etc. So I think it should be napari/acquire (for example), but we just don't fill in the full path.

@jni
Copy link
Member

jni commented Jun 12, 2024

Just realized it's going to be messy having the keys be just the menu name with absolutely no prefix, because of potential clashes with other plugin submenus etc. So I think it should be napari/acquire (for example), but we just don't fill in the full path.

We control the contributable menus, so we can make sure there are no name collisions there. We can also decide on a resolution order or raising if a name clashes and is not fully qualified. So, if napari-special has an edit menu, they should refer to it as napari-special/edit, or it will end up in our own menu.

@DragaDoncila
Copy link
Contributor Author

We control the contributable menus, so we can make sure there are no name collisions there.

Yeah, but I think explicit is better than implicit, and it's nice to have the menus either start with napari, or the plugin's own name

@jni
Copy link
Member

jni commented Jun 12, 2024

Yeah, but I think explicit is better than implicit, and it's nice to have the menus either start with napari, or the plugin's own name

no, it is a nightmare to type "napari" or the plugin name umpteen times. That is one of my manifest gripes, you'll recall, and moving it from command-id to the menus is really poor consolation...

If we can come up with a shortcut for the plugin's own menu, that would be fine — so it's either nothing (napari contributable), or self — the plugin's menu. (or own?)

And toggle visibility surely would be in this menu?

Unless it goes in top level edit?

I would find that super weird. Maybe a View top level menu?

Also, we could/should rename Layer > Edit to Layer > Modify.

BUT ALSO,

I think people will absolutely want to be editing their data and their metadata potentially so idk, unless you want to make some blanket rule like "every action returns a new layer" I'd rather have a dedicated spot for that stuff to go...

We don't have such a blanket rule and some widgets might want to have a toggle for in-place processing. So I actually don't think that they should all go in one place. For example, scikit-image morphology remove_small_objects could work both in place or return a copy, and users should be able to decide. I don't think these should be two separate commands.

@jni
Copy link
Member

jni commented Jun 12, 2024

You know nothing, Jon Snow. Functional inputs are totally possible,

but are they possible from plugins???

@DragaDoncila
Copy link
Contributor Author

but are they possible from plugins???

yes!!! 😜

@DragaDoncila
Copy link
Contributor Author

We don't have such a blanket rule and some widgets might want to have a toggle for in-place processing.

Yeah I think for widgets it's a bit different because I feel the user will probably be making choices and clicking buttons of their own volition. With commands I'm worried, because if someone clicks on something and expects a new layer but it actually edits the layer, that's a problem - especially since we don't have undo.

So for me that was kinda the guideline - put stuff in this edit menu and people should expect that it works on the layer data. But that's where I think we need to have more specific contribution types that guarantee things to the user about what's going to happen once they click this button.

Happy to think of other ways to organize them, with the key goal being I don't want users to experience trauma at the hands of an overzealous command.

@lucyleeow
Copy link
Collaborator

I've uploaded a tentative new manifest here for us to have a look at and make sure we're talking about the same thing.

That's exactly what I was thinking. Since there are other sections (sample URI, theme) as well as submenu, I don't mind there being a submenu section.

Edit - in-place vs new layer

With commands I'm worried, because if someone clicks on something and expects a new layer but it actually edits the layer, that's a problem - especially since we don't have undo.

and

For example, scikit-image morphology remove_small_objects could work both in place or return a copy, and users should be able to decide. I don't think these should be two separate commands.

@jni when you say these shouldn't be two separate commands, do you mean that this would be one (contributable) menu item but there would be a pop-up asking what the user wants to do (in-place to return new layer?). Or do you think there should be 2 menu items? How do you see this working?

Menu paths - full vs partial

First, I have forgotten, but have we decided if a plugin should be able to contribute to another plugins menu?

no, it is a nightmare to type "napari" or the plugin name umpteen times. That is one of my manifest gripes, you'll recall, and moving it from command-id to the menus is really poor consolation...

If we can come up with a shortcut for the plugin's own menu, that would be fine — so it's either nothing (napari contributable), or self — the plugin's menu. (or own?)

@jni is it really that bad, and it would be for a few submenu items?

I think a plugin should be able to specify which menu an item goes into, which I don't think will be fixed by having a conflict resolution order. How do you propose a plugin be able to specify whether an item goes into napari menu or it's own menu, or even another plugins menu?

@jni
Copy link
Member

jni commented Jun 17, 2024

@jni when you say these shouldn't be two separate commands, do you mean that this would be one (contributable) menu item but there would be a pop-up asking what the user wants to do (in-place to return new layer?). Or do you think there should be 2 menu items? How do you see this working?

Well, for this specific example there is an extra parameter (the minimum object size) needed, so you will need a widget one way or another — and that widget would have a boolean "in-place" that would be off by default. For cases where you only need a boolean you could instead present two buttons "run in-place" "run and create new layer".

I think a plugin should be able to specify which menu an item goes into, which I don't think will be fixed by having a conflict resolution order. How do you propose a plugin be able to specify whether an item goes into napari menu or it's own menu, or even another plugins menu?

  • foo -> goes in napari's foo menu
  • self/foo -> goes in current plugin's foo menu
  • other/foo -> goes in other plugin's foo menu

ie, we make napari a privileged set of menus, everything else must be specified, but with a shortcut for self.

@lucyleeow
Copy link
Collaborator

For cases where you only need a boolean you could instead present two buttons "run in-place" "run and create new layer".

Two buttons - are you talking about when the action is a widget? Is it possible that the action is just a menu item? In that case should there be two menu items?

@jni
Copy link
Member

jni commented Jun 18, 2024

I'm envisioning that in the future, if, say, a command requires an integer, selecting that command from the menu will bring up a short-lived pop-up to select the value, then you click "run" and the dialog disappears and the command runs, rather than a full-on docked widget, which is more suited to long-lasting interactions, rather than single runs of a thing. (The converse could also happen: don't use a dialog, dock the widget, but by default hitting "run" disappears the widget.)

In this world, having a single tick-box and a run button is not as good as having two buttons, "run ticked" and "run unticked".

@lucyleeow
Copy link
Collaborator

The converse could also happen: don't use a dialog, dock the widget, but by default hitting "run" disappears the widget.)

I think I prefer the dialog, which is what I was thinking above.

In this world, having a single tick-box and a run button is not as good as having two buttons, "run ticked" and "run unticked".

I this world - in the world where the menu item opens a docked widget?

I may have missed it, but what did we decide about commands that can become a widget or be a menu item?

@jni
Copy link
Member

jni commented Jun 18, 2024

In this world - in the world where the menu item opens a docked widget?

in the the world where a menu item will open a (probably magicgui-generated) dialog if we can't automatically determine the value of all the arguments to the function from the current context.

I may have missed it, but what did we decide about commands that can become a widget or be a menu item?

I don't know that it's been decided, but the macOS convention is that you have an action normally and if you hold down Option the menu item changes from Title to Other Title… and you get a widget. (In general, any menu item that will require a dialog should have a "…". See for example Firefox > Bookmarks > Bookmark All Tabs…) See e.g. Pages:

menu-opt.mp4

I think currently most plugins are widgets one way or another, so we can just leave everything as a widget and work with the community to have most actions be contextual, and holding option for specifying parameters. Dock Widgets would be their own thing.

@lucyleeow
Copy link
Collaborator

in the the world where a menu item will open a (probably magicgui-generated) dialog

Oh the buttons would be in the pop up dialog? That makes more sense

@DragaDoncila DragaDoncila marked this pull request as ready for review June 21, 2024 05:28
@jni
Copy link
Member

jni commented Jun 22, 2024

Since this PR does not affect the NAP status, and we've reached something close to a stable state, I'm going to merge so the document can be in published form for (hopefully final) discussion and acceptance. 😊 Massive thanks to Draga for all the work so far and @lucyleeow for the discussion — which has helped make the document and our own brains clearer! 😊

@jni jni merged commit c1bf91b into napari:main Jun 22, 2024
11 checks passed
jni added a commit to napari/napari that referenced this pull request Jun 26, 2024
# References and relevant issues

# Description
This PR is an alpha implementation of NAP-6. It adds all proposed menus
from NAP-6 (see docs PR for [latest
status](napari/docs#312)) and enables plugins to
add commands and widgets to the proposed menus.
Right now it's in pretty early stages, and not everything is implemented
- ~~I'll be opening a tracking issue soon~~ see the [tracking
issue](#7012).

To try it out, you can just install from this branch, but most menus
will be empty. I've added actions for the new layer buttons to `File ->
New Layer`, but haven't yet populated other built-in stuff. You can also
run the script below to add actions on the fly. Finally, you can[
install this
branch](https://github.com/DragaDoncila/napari-demo/tree/menu-contrib)
of `napari-demo` to see a plugin declare some menus.

```python
import napari
from napari._app_model import get_app
from app_model.types import Action

viewer = napari.Viewer()

def large_points() -> napari.layers.Points:
    return napari.layers.Points(size=40)

actions = [
    Action(
        id='napari-example.merge_labels',
        title='Merge Labels... (napari-example)',
        callback=lambda: napari.utils.notifications.show_info("Not implemented..."),
        menus=[{'id': 'napari/layers/annotate'}]
    ),
    Action(
        id='napari-example.large_points',
        title='Large Points (napari-example)',
        callback=large_points,
        menus=[{'id': 'napari/file/new_layer'}]
    )

]

app = get_app()
app.register_actions(actions)
napari.run()
```

@jni thinks that this is ~ready for an alpha rc, but there's still no
tests or docs really yet.

---------

Co-authored-by: Juan Nunez-Iglesias <jni@fastmail.com>
Co-authored-by: napari-bot <napari-bot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants