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]
```
:::
::::
|