File: _npe2_widgets_guide.md.jinja

package info (click to toggle)
python-npe2 0.7.8-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 828 kB
  • sloc: python: 7,088; makefile: 19
file content (151 lines) | stat: -rw-r--r-- 4,876 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
(widgets-contribution-guide)=
## Widgets

Widget plugin contributions allow developers to add novel graphical
elements (aka "widgets") to the user interface. For a full introduction to
creating `napari` widgets see [](creating-widgets).

Widgets can request
access to the viewer instance in which they are docked, enabling a broad
range of functionality: essentially, anything that can be done with the
napari `Viewer` and `Layer` APIs can be accomplished with widgets.

```{important}
Because this is a powerful and open-ended plugin specification, we
ask that plugin developers take additional care when providing widget plugins.
Make sure to only use public methods on the `viewer` and `layer` instances.
Also, be mindful of the fact that the user may be using your plugin along with
other plugins or workflows: try to only modify layers added by your plugin, or
specifically requested by the user.
```

The widget specification requires that the plugin provide napari with a
*callable* object that, when called, returns an *instance* of a widget.
Here "widget" means a subclass of `QtWidgets.QWidget` or `magicgui.widgets.Widget`,
or a `FunctionGui`. Additionally, the plugin can provide an arbitrary function if using
'autogenerate', which requests that napari autogenerate a widget using
`magicgui.magicgui` (see item 3 below).

There are a few commonly used patterns that fulfill this `Callable[..., Widget]`
specification:

1. Provide a `class` object that is a subclass of `QtWidgets.QWidget` or
   `magicgui.widgets.Widget`:

   ```python
   from qtpy.QtWidgets import QWidget

   class MyPluginWidget(QWidget):
       def __init__(self, viewer: 'napari.viewer.Viewer', parent=None):
           super().__init__(parent)
           self._viewer = viewer
   ```

2. Provide a `magicgui.magic_factory` object:

    ```python
    from magicgui import magic_factory

    @magic_factory
    def create_widget(image: 'napari.types.ImageData') -> 'napari.types.ImageData':
        ...
    ```

    *(reminder, in the example above, each time the `magic_factory`-decorated
    `create_widget()` function is called, it returns a new widget instance ––
    just as we need for the widget specification.)*

3. Lastly, you can provide an arbitrary function and request that napari
   autogenerate a widget using `magicgui.magicgui`.  In the first generation
   `napari_plugin_engine`, this was the `napari_experimental_provide_function`
   hook specification.  In the new `npe2` pattern, one uses the `autogenerate`
   field in the [WidgetContribution](contributions-widgets).

For more examples see [](creating-widgets) and
[GUI gallery examples](https://napari.org/stable/_tags/gui.html) (only a subset
involve widgets). Additionally,
[napari-plugin-template](https://github.com/napari/napari-plugin-template)
has more robust widget examples that you can adapt to your needs.

```{note}
Notice that `napari` type annotations are strings and not imported. This is to
avoid including `napari` as a plugin dependency when not strictly required.
```

### Widget example

::::{tab-set}
:::{tab-item} npe2
**python implementation**

```python
# example_plugin.some_module
{{ 'widgets'|example_implementation }}
```

**manifest**

See [Widgets contribution reference](contributions-widgets) for field details.

```yaml
{{ 'widgets'|example_contribution }}
```
:::

:::{tab-item} napari-plugin-engine

```{admonition} Deprecated!
This demonstrates the now-deprecated `napari-plugin-engine` pattern.
```

**python implementation**

[hook_specification](https://napari.org/stable/plugins/npe1.html#napari.plugins.hook_specifications.napari_experimental_provide_dock_widget)

```python
from qtpy.QtWidgets import QWidget
from napari_plugin_engine import napari_hook_implementation


class AnimationWizard(QWidget):
    def __init__(self, viewer: "napari.viewer.Viewer", parent=None):
        super().__init__(parent)
        ...


@magic_factory
def widget_factory(
    image: "napari.types.ImageData", threshold: int
) -> "napari.types.LabelsData":
    """Generate thresholded image.

    This pattern uses magicgui.magic_factory directly to turn a function
    into a callable that returns a widget.
    """
    return (image > threshold).astype(int)


def threshold(
    image: "napari.types.ImageData", threshold: int
) -> "napari.types.LabelsData":
    """Generate thresholded image.

    This function will be turned into a widget using `autogenerate: true`.
    """
    return (image > threshold).astype(int)


# in the first generation plugin engine, these widgets were declared
# using special `napari_hook_implementation`-decorated functions.

@napari_hook_implementation
def napari_experimental_provide_dock_widget():
    return [AnimationWizard, widget_factory]


@napari_hook_implementation
def napari_experimental_provide_function():
    return [threshold]
```
:::
::::