Skip to content

Commit

Permalink
Add page on magicgui type registration (#399)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
lucyleeow authored Apr 16, 2024
1 parent 68d7a86 commit 2c09df0
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ subtrees:
- file: developers/architecture/dir_organization
- file: developers/architecture/napari_models
- file: developers/architecture/app_model
- file: developers/architecture/magicgui_type_reg
- file: naps/index
subtrees:
- maxdepth: 1
Expand Down
2 changes: 2 additions & 0 deletions docs/developers/architecture/app_model.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ at runtime (see [issue 6600](https://github.com/napari/napari/issues/6600)
for more on this). Note that it is likely that this pull request will be split into
smaller pull requests for ease of review and better git history.

(app_model_dep_inj_result)=

## Dependency injection and result processing

Dependency injection allows to write functions using parameter type annotations,
Expand Down
3 changes: 3 additions & 0 deletions docs/developers/architecture/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ code base. For advanced napari usage documentation, see [](explanations).
connected to Qt classes and Vispy classes.
- [](app-model): Explains the napari application model, a declarative schema for
keeping track of commands, menus and keybindings of the napari GUI.
- [](magicgui_type_registration): Explains how `magicgui` widgets declared by users
or plugins are automatically created, inputs updated and outputs added to the
`Viewer` for registered `napari` types.
80 changes: 80 additions & 0 deletions docs/developers/architecture/magicgui_type_reg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
(magicgui_type_registration)=

# `magicgui` type registration

[`magicgui`](https://pyapp-kit.github.io/magicgui/) uses
[type hints](https://peps.python.org/pep-0484/) to infer the appropriate widget type
for a given function parameter. It allows third party packages
(like `napari`) to [register](https://pyapp-kit.github.io/magicgui/type_map/#registering-support-for-custom-types) support for their types using
{func}`~magicgui.type_map.register_type`. `napari` registers
a number of types, additionally specifying, where appropriate:

* widget type for the parameter type
* function for updating inputs for the widget
* return callback, for return types

This enables `magicgui` widgets to be easily created via type annotations.

```{note}
This page provides implementation details on `napari`-specific type registration
in `magicgui` and is aimed at `napari` developers.
For information about using `magicgui` (for users and plugin developers) see
[](creating-widgets).
```

## Registration details

`napari` types are either registered via the
{func}`@register_type <magicgui.type_map.register_type>` decorator when they are
defined or in
[`napari/types.py`](https://github.com/napari/napari/blob/main/napari/types.py).
For the full list of types registered, see [](magicgui-parameter-annotations).

### Layer types

The 'layer' types registered are {class}`~napari.layers.Layer` and its subclasses, and
`<LayerType>Data` types.
All these types provide a `choices` callable when they are registered with
`magicgui`. This means that annotating with these types creates a
{class}`~magicgui.widgets.bases.CategoricalWidget`, which will have a dropdown
selection whose options will be updated via the
`choices` callable.

This callable is either `get_layers_data` or `get_layers`.
These functions retrieve the closest parent `Viewer` of the native
{class}`~magicgui.widgets.bases.CategoricalWidget` widget and returns a list of
{class}`~napari.layers.Layer` or tuple of format `('layer name', <LayerType>Data)`.
This callable is set to the `choices` attribute of the
{class}`~magicgui.widgets.bases.CategoricalWidget` in its
{meth}`~magicgui.widgets.bases.CategoricalWidget.reset_choices` method and thus
gets called via {class}`~magicgui.widgets.bases.CategoricalWidget`'s `choices` setter.
`napari` {meth}`~napari.qt.Window.add_dock_widget` checks if the dock widget has
a `reset_choices` attribute and if so, connects it to layer events.

Note that `magicgui` {class}`~magicgui.widgets.bases.ContainerWidget`'s will call
`reset_choices` on all subwidgets. This means that when the dock widget is a
{class}`~magicgui.widgets.bases.ContainerWidget`, any subwidgets (e.g.,
{class}`~magicgui.widgets.bases.CategoricalWidget`s) are updated
whenever layers change.
**Note that {meth}`~napari.qt.Window.add_dock_widget` will
connect any existing `reset_choices` widget attribute to layer events for all widgets,
not just `magicgui` widgets.**

The 'layer' types also specify a `return_callback` function that adds the layer
to the closest parent `Viewer` of the native widget when a 'layer' type is a return
annotation.

### `Viewer` type

{class}`~napari.viewer.Viewer` differs from the layer types. `napari` simply specifies
that the closest parent {class}`~napari.viewer.Viewer` (technically a public proxy of
the {class}`~napari.viewer.Viewer` that prevents private attribute access) be bound to
the widget (technically it's bound to a hidden child
{class}`~magicgui.widgets.EmptyWidget`). This allows the {class}`~napari.viewer.Viewer`
to be used in the `magicgui` widget.

```{note}
When widget is not a `magicgui` widget, the {class}`~napari.viewer.Viewer` is provided
via a wrapper `napari` adds around widget contributions.
```

0 comments on commit 2c09df0

Please sign in to comment.