Plugin contributions#

This page provides guides on many of the plugin contribution patterns. Each provides a general overview of the purpose of the contribution and an example implementation. For details on the type and meaning of each field in a specific contribution, See the contributions reference

Readers#

Reader plugins may add support for new filetypes to napari. They are invoked whenever viewer.open('some/path') is used on the command line, or when a user opens a file in the graphical user interface by dropping a file into the canvas, or using File -> Open...

Introduction to the reader contribution#

napari’s reading process is motivated by the idea that a plugin should only attempt to read a file once we are fairly confident that this process will not fail. The determination of whether a plugin can read a file is based on two checks:

  • The filename_patterns field of the manifest contribution. If the given path does not match any of the specified filename_patterns, the plugin will never be given the file

  • The command provided by the reader contribution. The plugin developer should use this function, sometimes called napari_get_reader or get_reader, to check various properties and attributes of the file at the given path to determine whether it can be read

The get_reader command provided by a reader contribution is expected to be a function that accepts a path (str) or a list of paths and:

  • returns None (if it does not want to accept the given path)

  • returns a new function that is capable of doing the reading.

Why do we need two functions?

The get_reader command should make as many checks as possible (without loading the full file) to determine if it can read the path. For example, you might check for the presence of specific pointer files (like a zarr.json), or call a file format validating function to ensure the file is well-formed before reading, or inspect a few bytes at the beginning of the file to make sure it’s the right file format.

Another benefit of the get_reader function is that it allows the plugin developer to define multiple different reading functions depending on the file format or its properties, and return the appropriate one for the given path.

This function should not raise exceptions, as napari has its own handlers that check for available compatible readers, and surface this information to the user. The ReaderFunction (described below), can raise errors, and napari will surface any raised errors to the user.

The ReaderFunction will be passed the same path (or list of paths) and is expected to return a list containing LayerData tuples or a fully instantiated napari Layer objects like Image or Labels. Formally, the ReaderFunction type is specified as:

ReaderFunction = Callable[[PathOrPaths], List[LayerData]]

In the rare case that a reader plugin would like to “claim” a file, but not actually add any data to the viewer, the ReaderFunction may return the special value [(None,)].

Accepting directories

A reader may indicate that it accepts directories by setting contributions.readers.<reader>.accepts_directories to True; otherwise, they will not be invoked when a directory is passed to viewer.open.

Reader example#

python implementation

# example_plugin.some_module
PathLike = str | pathlib.Path
PathOrPaths = PathLike | Sequence[PathLike]
ReaderFunction = Callable[[PathOrPaths], list[LayerData]]


def get_reader(path: "PathOrPaths") -> Optional["ReaderFunction"]:
    # If we recognize the format, we return the actual reader function
    if isinstance(path, str) and path.endswith(".xyz"):
        return xyz_file_reader
    # otherwise we return None.
    return None


def xyz_file_reader(path: "PathOrPaths") -> list["LayerData"]:
    data = ...  # somehow read data from path
    layer_attributes = {"name": "etc..."}
    return [(data, layer_attributes)]

manifest

See Readers contribution reference for field details.

contributions:
  commands:
  - id: example-plugin.read_xyz
    title: Read ".xyz" files
    python_name: example_plugin.some_module:get_reader
  readers:
  - command: example-plugin.read_xyz
    filename_patterns:
    - '*.xyz'
    accepts_directories: false

Deprecated!

This demonstrates the now-deprecated napari-plugin-engine pattern.

python implementation

hook specification

from napari_plugin_engine import napari_hook_implementation


@napari_hook_implementation
def napari_get_reader(path: PathOrPaths) -> Optional[ReaderFunction]:
    # If we recognize the format, we return the actual reader function
    if isinstance(path, str) and path.endswith(".xyz"):
        return xyz_file_reader
    # otherwise we return None.
    return None


def xyz_file_reader(path: PathOrPaths) -> List[LayerData]:
    data = ...  # somehow read data from path
    layer_properties = {"name": "etc..."}
    return [(data, layer_properties)]

Writers#

Writer plugins add support for exporting data from napari. They are invoked whenever viewer.layers.save('some/path.ext') is used on the command line, or when a user requests to save one or more layers in the graphical user interface with File -> Save Selected Layer(s)... or Save All Layers...

Important

This guide describes the second generation (npe2) plugin specification. New plugins should no longer use the old napari_get_writer hook specification from the first generation napari_plugin_engine.

Writer plugin function signatures#

Writer plugins are functions that:

  1. Accept a destination path and data from one or more layers in the viewer

  2. Write layer data and associated attributes to disk

  3. Return a list of strings containing the path(s) that were successfully written.

They must follow one of two calling conventions (where the convention used is determined by the layer_type constraints provided by the corresponding writer contribution in the manifest).

1. single-layer writer#

Single-layer writers will receive a path, layer data, and a dict of layer attributes, (e.g. {'name': 'My Layer', 'opacity': 0.6})

def single_layer_writer(path: str, data: Any, attributes: dict) -> List[str]:
    ...

The formal type is as follows:

DataType = Any  # usually something like a numpy array, but varies by layer
LayerAttributes = dict
SingleWriterFunction = Callable[[str, DataType, LayerAttributes], List[str]]

2. multi-layer writer#

Multi-layer writers will receive a path, and a list of full layer data tuples.

def multi_layer_writer(path: str, layer_data: List[FullLayerData]) -> List[str]:
    ...

The formal type is as follows:

DataType = Any  # usually something like a numpy array, but varies by layer
LayerAttributes = dict
LayerName = Literal["graph", "image", "labels", "points", "shapes", "surface", "tracks", "vectors"]
FullLayerData = Tuple[DataType, LayerAttributes, LayerName]
MultiWriterFunction = Callable[[str, List[FullLayerData]], List[str]]

Layer type constraints#

Individual writer contributions are determined to be single-layer writers or multi-layer writers based on their writer.layer_types constraints provided in the contribution metadata.

A writer plugin declares that it can accept between m and n layers of a specific type (where 0 ≤ m ≤ n), using regex-like syntax with the special characters ?, + and *:

  • image: Writes exactly 1 image layer.

  • image?: Writes 0 or 1 image layers.

  • image+: Writes 1 or more image layers.

  • image*: Writes 0 or more image layers.

  • image{k}: Writes exactly k image layers.

  • image{m,n}: Writes between m and n layers (inclusive range). Must have m <= n.

A writer plugin will only be invoked when its layer_types constraint is compatible with the layer type(s) that the user is saving. When a type is not present in the list of constraints, it is assumed the writer is not compatible with that type.

Consider this example contributions section in a manifest:

contributions:
  writers:
  - command: example-plugin.some_writer
    layer_types: ["image+", "points?"]
    filename_extensions: [".ext"]

This writer would be considered when 1 or more Image layers and 0 or 1 Points layers are selected (i.e. the Points layer is optional). This writer would not be selected when the user tries to save an image and a vectors layer, because vectors is not listed in the layer_types.

Writer example#

python implementation

# example_plugin.some_module
def write_points(path: str, layer_data: Any, attributes: dict[str, Any]) -> list[str]:
    with open(path, "w"):
        ...  # save layer_data and attributes to file

    # return path to any file(s) that were successfully written
    return [path]

manifest

See Writers contribution reference for field details.

contributions:
  commands:
  - id: example-plugin.write_points
    title: Save points layer to csv
    python_name: example_plugin.some_module:write_points
  writers:
  - command: example-plugin.write_points
    layer_types:
    - points
    filename_extensions:
    - .csv

Deprecated!

This demonstrates the now-deprecated napari-plugin-engine pattern.

python implementation

hook specification

from napari_plugin_engine import napari_hook_implementation

@napari_hook_implementation
def napari_write_points(path: str, data: Any, meta: dict) -> Optional[str]:
    """Write points data and metadata into a path.

    Parameters
    ----------
    path : str
        Path to file, directory, or resource (like a URL).
    data : array (N, D)
        Points layer data
    meta : dict
        Points metadata.

    Returns
    -------
    path : str or None
        If data is successfully written, return the ``path`` that was written.
        Otherwise, if nothing was done, return ``None``.
    """

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:

    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:

    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.

For more examples see Creating widgets and GUI gallery examples (only a subset involve widgets). Additionally, 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#

python implementation

# example_plugin.some_module
Widget = Union["magicgui.widgets.Widget", "qtpy.QtWidgets.QWidget"]


class MyWidget(QWidget):
    """Any QtWidgets.QWidget or magicgui.widgets.Widget subclass can be used."""

    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)

manifest

See Widgets contribution reference for field details.

contributions:
  commands:
  - id: example-plugin.my_widget
    title: Open my widget
    python_name: example_plugin.some_module:MyWidget
  - id: example-plugin.threshold_widget
    title: Make threshold widget with magic_factory
    python_name: example_plugin.some_module:widget_factory
  - id: example-plugin.do_threshold
    title: Perform threshold on image, return new image
    python_name: example_plugin.some_module:threshold
  widgets:
  - command: example-plugin.my_widget
    display_name: Wizard
  - command: example-plugin.threshold_widget
    display_name: Threshold
  - command: example-plugin.do_threshold
    display_name: Threshold
    autogenerate: true

Deprecated!

This demonstrates the now-deprecated napari-plugin-engine pattern.

python implementation

hook_specification

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]

Sample Data#

This contribution point allows plugin developers to contribute sample data that will be accessible in the napari interface via the File > Open Sample menu, or via the command line with viewer.open_sample.

Sample data can be useful for demonstrating the functionality of a given plugin. It can take the form of a Sample Data URI that points to a static resource (such as a file included in the plugin distribution, or a remote resource), or Sample Data Function that generates layer data on demand.

Note that unlike reader contributions, sample data contributions are always expected to return data, so returning [(None,)] will cause an error.

Sample Data example#

python implementation

# example_plugin.some_module
def create_fractal() -> list["LayerData"]:
    """An example of a  Sample Data Function.

    Note: Sample Data with URIs don't need python code.
    """
    data = ...  # do something cool to create a fractal
    return [(data, {"name": "My cool fractal"})]

manifest

See Sample Data contribution reference for field details.

contributions:
  commands:
  - id: example-plugin.data.fractal
    title: Create fractal image
    python_name: example_plugin.some_module:create_fractal
  sample_data:
  - command: example-plugin.data.fractal
    key: fractal
    display_name: Fractal
  - key: napari
    display_name: Tabueran Kiribati
    uri: https://en.wikipedia.org/wiki/Napari#/media/File:Tabuaeran_Kiribati.jpg

Deprecated!

This demonstrates the now-deprecated napari-plugin-engine pattern.

python implementation

hook specification

import numpy as np
from napari_plugin_engine import napari_hook_implementation

def _generate_random_data(shape=(512, 512)):
    data = np.random.rand(*shape)
    return [(data, {'name': 'random data'})]

@napari_hook_implementation
def napari_provide_sample_data():
    return {
        'random data': _generate_random_data,
        'random image': 'https://picsum.photos/1024',
        'sample_key': {
            'display_name': 'Some Random Data (512 x 512)'
            'data': _generate_random_data,
        }
    }

Configurations#

Plugin settings allow your plugin to expose user-configurable options in napari’s Preferences dialog. They are declared in the manifest under contributions.configurations and are rendered automatically — no Python code is needed to build the settings UI.

Configuration contribution values are exposed at runtime via napari.settings.get_plugin_settings("example-plugin") and persisted to <config dir>/example-plugin.yaml. Settings are written to disk automatically whenever a value changes.

A minimal example#

contributions:
  configurations:
    pre_processing_widget:
      title: Pre-processing Widget
      properties:
        colormap:
          title: Default colormap
          type: string
          default: gray
          description: Colormap applied to output images.
          enum:
          - gray
          - green
          - viridis
        gamma:
          title: Gamma correction
          type: number
          default: 1.0
          description: Gamma value applied to output images.
          minimum: 0.1
          maximum: 2.0
    reader:
      title: Reader
      properties:
        max_size_mb:
          title: Max in-memory image size (MB)
          type: integer
          default: 512
          description: Images larger than this are lazy-loaded via dask instead of
            being loaded eagerly.
          minimum: 1
          maximum: 4096
        auto_contrast:
          title: Auto-contrast on open
          type: boolean
          default: true
          description: Turn on auto-contrast on all opened layers.

Each entry in contributions.configurations is a category of settings, keyed by a configuration key. The example above declares two categories, pre_processing_widget and reader, each with its own properties.

ConfigurationContribution or Categories#

Each ConfigurationContribution, or category of setting, is declared with a key in the manifest e.g. pre_processing_widget or reader. These keys must be valid, non-reserved Python identifiers that do not begin with an underscore (see Validation), and must be unique within a manifest. These keys are never shown in the UI, but are used to access your ConfigurationContribution programmatically at get_plugin_settings("example-plugin").<configuration-key>.

The title of the ConfigurationContribution is displayed as a section header on the plugin’s preferences page.

ConfigurationProperty or Fields#

ConfigurationContributions contain one or more ConfigurationProperty. Each ConfigurationProperty is an individual setting or field the user can configure. Each ConfigurationProperty has a key in the manifest. These keys must be valid, non-reserved Python identifiers that do not begin with an underscore (see Validation), and must be unique within their ConfigurationContribution. These keys are never shown in the UI, but are used to access your ConfigurationContribution programmatically at get_plugin_settings("example-plugin").<configuration-key>.<property-key>.

The title of the ConfigurationProperty is displayed as a label to the left of the widget that allows the user to configure the value for this field.

To access an individual field in your plugin preferences:

from napari.settings import get_plugin_settings

my_settings = get_plugin_settings("example-plugin")
my_settings.reader.max_size_mb               # <category key>.<property key>
my_settings.pre_processing_widget.colormap

Each ConfigurationProperty must have a set title, type and default value.

Types & Constraints#

The type declared for a ConfigurationProperty determines the widget that will be created to allow the user to set the value of this property.

We currently only support simple types like boolean, integer, float or string. Depending on the type you’ve selected, you can also set basic constraints to validate the user’s provided value e.g. for integer types you can set a minimum and a maximum value. For the full list of available types, and their constraints, see the ConfigurationProperty reference.

enum is supported on any property type (it renders a dropdown), not just strings. Both YAML styles are valid — a flow list (enum: [gray, green, viridis]) or a block list (as in the example above); the block style is clearer when there are many options.

A default is required for every property. Constraints are enforced by the settings model, so invalid values cannot be saved.

How settings render in napari#

For the example plugin above, the Preferences dialog shows one page per plugin, with each category rendered as its own group:

Preferences
  ...              ── existing napari sections ──
  ----Plugin Preferences----
  Example Plugin   ◄── page per plugin (display_name)

  ╔═ Pre-processing Widget ══════════════╗   ◄── category title
  ║  Default colormap          [dropdown] ║   ◄── property title + widget
  ║  Gamma correction               [1.0] ║
  ╚══════════════════════════════════════╝
  ╔═ Reader ═════════════════════════════╗
  ║  Max in-memory image size (MB)  [512] ║
  ║  Auto-contrast on open            [x] ║
  ╚══════════════════════════════════════╝

The title of each category becomes a group-box header, and each property renders as a labelled input (title: label + widget chosen by type:). The property description: becomes the widget’s help/tooltip text.

Events: Reacting to settings changes#

Settings are napari EventedModels, so changing a value emits a psygnal event that you can connect to. Each category exposes an events emitter group with one emitter per setting:

my_settings = get_plugin_settings("example-plugin")

def on_max_size_changed(event):
    print("max size is now", event.value)

my_settings.reader.events.max_size_mb.connect(on_max_size_changed)

my_settings.reader.max_size_mb = 1024  # event causes a print: "max size is now 1024"

The top-level settings object also re-emits every change as a single changed event carrying a dotted <category>.<property> key, so one connection covers all of a plugin’s settings:

my_settings.events.changed.connect(lambda e: print(e.key, e.value))
# after the assignment above -> "reader.max_size_mb 1024"

These events fire whether a value is changed in code or in the Preferences dialog, and the change is auto-saved to the config file.

Validation#

npe2 validates that configuration keys and property keys are valid, non-reserved Python identifiers that do not begin with an underscore (they are used verbatim as attribute names on the generated settings model), and that every property has a valid type, default, and constraints.

Testing your settings#

In a normal napari session your installed plugin is discovered and get_plugin_settings("example-plugin") just works. For testing, use npe2’s npe2pm pytest fixture for a clean plugin manager, and napari’s plugin_settings pytest fixture for fresh settings in each test. These two fixtures will be available to you if you have napari and npe2 installed in your testing environment.

from pathlib import Path

# the plugin's actual napari.yaml; adjust the path for your package layout
MANIFEST_PATH = (
    Path(__file__).parent.parent / 'src' / 'example_plugin' / 'napari.yaml'
)


# plugin_settings fixture is exposed by napari
# npe2pm fixture is exposed by npe2
def test_plugin_settings_defaults_and_updates(plugin_settings, npe2pm):
    # with npe2pm fixture this plugin is scoped only to this test;
    # tmp_plugin accepts a path to a manifest file
    with npe2pm.tmp_plugin(manifest=MANIFEST_PATH):
        # with plugin_settings fixture, these settings are scoped
        # only to this test
        from napari.settings import get_plugin_settings
        settings = get_plugin_settings('example-plugin')

        # defaults come straight from the manifest
        assert settings.reader.lazy is False
        assert settings.reader.max_size_mb == 512

        # changing a value auto-saves to the yaml file at settings.config_path
        settings.reader.max_size_mb = 1024
        assert settings.reader.max_size_mb == 1024
        assert 'max_size_mb: 1024' in settings.config_path.read_text()

    ...

Supporting older napari#

Declared contributions.configurations are ignored by older versions of napari and npe2. However, the get_plugin_settings API, and automatic GUI generation, is only available in napari>=0.9.0.

If you need to support older napari, feature-detect the API and fall back to defaults — you don’t need a full settings shim, only the values your code actually reads.

from types import SimpleNamespace

# Defaults mirroring your manifest (only the values your code reads).
_DEFAULTS = SimpleNamespace(
    reader=SimpleNamespace(max_size_mb=512, auto_contrast=True),
)

def my_settings():
    """Return napari's settings, or defaults on napari < 0.9.0."""
    try:
        from napari.settings import get_plugin_settings
        return get_plugin_settings("example-plugin")
    except ImportError:
        return _DEFAULTS

If you only read one or two settings, inline the fallback instead:

def max_size_mb() -> float:
    try:
        from napari.settings import get_plugin_settings
        return get_plugin_settings("example-plugin").reader.max_size_mb
    except ImportError:
        return 512.0

The LayerData tuple#

When transferring data to and from plugins, napari does not pass Layer objects directly. Instead, it passes (mostly) pure-python and array-like types, deconstructed into a tuple that we refer to as a LayerData tuple. This type shows up often in plugins and is explained here.

Note that when writing your own plugin, type annotations are optional, except in the case of magicgui function widgets. For several types related to LayerData tuples, napari defines a type alias which better indicates a value’s functional role in a plugin. We describe these below.

Informal description#

(data, [attributes, [layer_type]])

A LayerData tuple is a tuple of length 1, 2, or 3 whose items, in order, are:

  1. The data object that would be used for layer.data (such as a numpy array for the Image layer)

  2. (Optional). A dict of layer attributes, suitable for passing as keyword arguments to the corresponding layer constructor (e.g. {'opacity': 0.7})

  3. (Optional). A lower case str indicating the layer type (e.g.'image', 'labels', etc…). If not provided (i.e. if the tuple is only of length 2), the layer type is assumed to be 'image’.

Formal type definition#

Formally, the typing for LayerData looks like this:

LayerData = Union[Tuple[DataType], Tuple[DataType, LayerProps], FullLayerData]

where …

from typing import Literal, Protocol, Sequence

LayerTypeName = Literal[
    'image', 'labels', 'points', 'shapes', 'surface', 'tracks', 'vectors'
]
LayerProps = Dict
DataType = Union[ArrayLike, Sequence[ArrayLike]]
FullLayerData = Tuple[DataType, LayerProps, LayerTypeName]
LayerData = Union[Tuple[DataType], Tuple[DataType, LayerProps], FullLayerData]


# where "ArrayLike" is very roughly ...
class ArrayLike(Protocol):
    shape: Tuple[int, ...]
    ndim: int
    dtype: np.dtype

    def __array__(self) -> np.ndarray: ...
    def __getitem__(self, key) -> ArrayLike: ...


# the main point is that we're more concerned with structural
# typing than literal array types (e.g. numpy, dask, xarray, etc...)

Examples#

Assume that data is a numpy array:

import numpy as np

data = np.random.rand(64, 64)

All of the following are valid LayerData tuples:

# the first three are equivalent, just an image array with default settings
(data,)
(data, {})
(data, {}, 'image')

# provide kwargs for image constructor
(data, {'name': 'My Image', 'colormap': 'red'})

# labels layer instead of image:
(data.astype(int), {'name': 'My Labels', 'blending': 'additive'}, 'labels')

Creation from a Layer instance.#

Note, the as_layer_data_tuple() method will create a layer data tuple from a given layer

>>> img = Image(np.random.rand(2, 2), colormap='green', scale=(4, 4))

>>> img.as_layer_data_tuple()
Out[7]:
(
    array([[0.94414642, 0.89192899],
       [0.21258344, 0.85242735]]),
    {
        'name': 'Image',
        'metadata': {},
        'scale': [4.0, 4.0],
        'translate': [0.0, 0.0],
        'rotate': [[1.0, 0.0], [0.0, 1.0]],
        'shear': [0.0],
        'opacity': 1,
        'blending': 'translucent',
        'visible': True,
        'experimental_clipping_planes': [],
        'rgb': False,
        'multiscale': False,
        'colormap': 'green',
        'contrast_limits': [0.2125834437981784, 0.9441464162780605],
        'interpolation': 'nearest',
        'rendering': 'mip',
        'experimental_slicing_plane': {'normal': (1.0, 0.0, 0.0), 'position': (0.0, 0.0, 0.0), 'enabled': False, 'thickness': 1.0},
        'iso_threshold': 0.5,
        'attenuation': 0.05,
        'gamma': 1
    },
    'image'
)

Adding to the viewer#

To add a LayerData tuple to the napari viewer, use :meth:Layer.create:

>>> image_layer_data = (data, {'name': 'My Image', 'colormap': 'red'}, 'image')
>>> viewer = napari.current_viewer()
>>> viewer.add_layer(napari.layers.Layer.create(*image_layer_data))

The only attribute that can’t be passed to napari.layers.Layer.create that is otherwise valid for a LayerData tuple is ‘channel_axis’.