Viewer tutorial#
Welcome to the tutorial on the napari viewer!
This tutorial assumes you have already installed napari and know how to launch the viewer. For help with installation see our installation tutorial. For help launching the viewer see our getting started tutorial.
This tutorial will teach you about the napari viewer, including how to use its graphical user interface (GUI) and how the data within it is organized. At the end of the tutorial, you should understand both the layout of the viewer on the screen and the data inside of it.
Launching the viewer#
As discussed in the getting started tutorial, the napari viewer can be launched from the command-line, a python script, an IPython console, or a Jupyter notebook. All four methods launch the same viewer, and anything related to interacting with the viewer on the screen applies equally to all of them. We will use the syntax for running the code inside a jupyter notebook with each code block below pasted into its own cell, but if you’d like to use a python script instead, simply copy and paste the code blocks into scripts with napari.run()
as the final line (this starts an event loop which will
open an interactive viewer) and run them.
Note: There is also an IPython console available in napari, when napari is launched from the terminal, from a Python script, or when you use the napari bundled app. You can open it with the IPython console button (far left viewer button) or with the menu option Window > console. You can use this console to programmatically interact with an open viewer using the API methods illustrated in this tutorial.
Let’s get started by launching a viewer with a simple 2D image.
The fastest way to open a viewer with an image on the canvas is using imshow
:
from skimage import data
import napari
viewer, image_layer = napari.imshow(data.astronaut(), rgb=True)
Calling imshow
will return a Viewer
object that is the main object inside napari and a Image
layer object. All the data you add to napari will be stored inside the Viewer
object and will be accessible from it. This command will also open the viewer to create a GUI that you can interact with. The Image
will contain information about the image and allow you to access image methods.
You can also create an empty Viewer
directly and then start adding images to it. For example:
from skimage import data
import napari
viewer = napari.Viewer()
new_layer = viewer.add_image(data.astronaut(), rgb=True)
add_image
accepts the same arguments as imshow
but only returns an Image
layer instead of both the Viewer
and Image
layer (as you must already have a viewer to use it).
After running either of those two commands, you should be able to see the photograph of the astronaut in the napari viewer as shown below:
Show code cell source
from napari.utils import nbscreenshot
nbscreenshot(viewer, alt_text="photograph of an astronaut in napari viewer")
imshow
and the add_image
methods accept any numpy-array like object as input, including n-dimensional arrays. For more information on adding images to the viewer see the image layer guide.
Now we will continue exploring the rest of the viewer.
Layout of the viewer#
The viewer is organized into a few key areas which are explained in the next sections:
Main Menu (top bar menu)
Layer Controls
Layer Buttons
Layer List
Viewer Buttons
Status Bar
Canvas
Dimension Sliders
Scroll Buttons
Frame Playback
The image below has the areas of the viewer labeled:
We’ll go through each of these in the next sections.
Canvas#
The canvas is in the center of the viewer and contains the visual display of the data passed to napari, including Images
, Points
, Shapes
, and other supported data types. Under the hood, the canvas is a vispy.scene.SceneCanvas
object which has built-in support for features such as zooming and panning. As vispy
uses OpenGL
and your graphics card, panning and zooming are highly performant. You can return to the original zoom level by clicking the home
button in the viewer buttons panel.
Layer list#
Layers are one of the basic napari objects. There are different layer types for Image
, Points
, Shapes
, and other data types. They can be added to the viewer either programmatically or through the GUI. Once added, they populate the layer list located on the bottom left side of the canvas.
The layer list contains one widget for each of the layers that have been added to the viewer and includes a thumbnail
that shows a miniaturized version of the currently viewed data, a name
that is an editable text box, a visibility
button (eye icon) that can be toggled on or off to show or hide the layer, and an icon
for the layer type.
Adding the following three image layers using the code below adds three-layer widgets to the layer list as follows:
import napari
from skimage import data
viewer = napari.Viewer()
viewer.add_image(data.astronaut(), name='astronaut')
viewer.add_image(data.moon(), name='moon')
viewer.add_image(data.camera(), name='camera')
Show code cell source
nbscreenshot(viewer, alt_text="3 image layers shown in napari viewer with the canvas displaying a photograph of a man looking through a camcorder")
Note that we’ve also named each of the layers using the name
keyword argument in add_image
, and that name appears as a string in the layer widget. The layer name is coerced into being unique so it can be used to index into the LayerList
.
You can select layers, which highlights them, by clicking on their layer widget. Multiple layers can be simultaneously selected using either shift
click to select all the layers in between two clicked-on layers or Ctrl
+click (Windows) or Command
+click to select just the clicked on layers respectively.
You can rearrange the order of the layers by dragging them, including dragging multiple layers at the same time.
The Viewer
object also contains the LayerList
object that allows access to the data of all the layers with:
viewer.layers
[<Image layer 'astronaut' at 0x7fb8f4331540>, <Image layer 'moon' at 0x7fb8ec324dc0>, <Image layer 'camera' at 0x7fb8ec2ef3d0>]
This object can be indexed like a normal list using an int
or using the str
name of the layer as follows:
viewer.layers[0]
<Image layer 'astronaut' at 0x7fb8f4331540>
viewer.layers['astronaut']
<Image layer 'astronaut' at 0x7fb8f4331540>
You can rearrange layers by clicking and dragging them.
Layer controls#
Above the layer list in the top left corner of the viewer there is a box that contains the layer controls. The controls that are available depend on the layer type selected.
For example, if you add a Points
layer after adding an Image
layer, the new Points
layer will be ‘selected’ and you will now see different controls.
import numpy as np
from skimage import data
import napari
viewer, image_layer = napari.imshow(data.astronaut(), rgb=True)
points = np.array([[100, 100], [200, 200], [300, 100]])
viewer.add_points(points, size=30)
Show code cell source
nbscreenshot(viewer, alt_text="points layer showing 3 white points layered on top of astronaut image in napari viewer")
Adjusting these properties in the layers list will cause corresponding changes to properties on the selected individual layers. These properties can also be changed and accessed in the console through viewer.layers
.
For example, the name and opacity of a layer can be changed within the console as follows:
viewer.layers[0].name = 'astronaut'
viewer.layers[0].opacity = 0.7
and these changes will instantly propagate to the GUI. For more information about the different properties for different layer types please see our layer specific tutorials listed at the bottom of this tutorial.
Dimension sliders#
One of the main strengths of napari is that it has been designed from the beginning to handle n-dimensional data. While much consumer photography is 2D and RGB
, scientific image data can often be volumetric (i.e. 3D), volumetric timeseries (i.e. 4D), or even higher dimensional. napari places no limits on the dimensionality of its input data for all its layer types.
Adding data with a dimensionality greater than 2D will cause dimension sliders to appear directly underneath the canvas and above the status bar. As many sliders as needed will appear to ensure the data can be fully browsed. For example, a 3D dataset needs one slider, a 4D dataset needs two sliders, and so on. The widths of the scroll bars of the dimension sliders are directly related to how many slices are in each dimension. To the left of each slider will be an integer indicating which dimension is being controlled by that slider. These integers are automatically updated when changing which dimensions are to be displayed. Alternately, the sliders can be labeled by double-clicking on the integer and editing the field. The labels can be retrieved programatically as follows:
# To get the dimension labels
viewer.dims.axis_labels
('0', '1')
You can also set the axis labels programatically as follows:
# To set new axis labels
viewer.dims.axis_labels = ("label_1", "label_2")
It is also possible to mix data of different shapes and dimensionality in different layers. If a 2D and 4D dataset are both added to the viewer then the sliders will affect only the 4D dataset, the 2D dataset will remain the same. Effectively, the two datasets are broadcast together using NumPy broadcasting rules.
For example, the following commands from the console will add both 2D and 3D datasets to the same viewer:
import numpy as np
from skimage import data
import napari
viewer = napari.Viewer()
viewer.add_image(data.moon(), name='moon')
blobs = np.stack(
[
data.binary_blobs(
length=512, blob_size_fraction=0.05, n_dim=2, volume_fraction=f
)
for f in np.linspace(0.05, 0.5, 10)
],
axis=0,
).astype(float)
viewer.add_image(blobs, name='blobs', opacity=0.5, colormap='red')
Show code cell source
nbscreenshot(viewer, alt_text="A 2d view of the moon on top of which is overlaid a 3d volume containing blobs through which you can navigate using the dimension slider.")
In this example there are three dimensions. In order to get or update the current position of the sliders, use:
# To get the current position returned as tuple of length 3
viewer.dims.current_step
(0, 255, 255)
And to change the current position of the sliders use:
# To change the current position of this example to step 3
viewer.dims.current_step = (3, 255, 255)
The length of the current_step
tuple corresponds to the number of dimensions. Note that in this example, the last two dimensions are displayed (don’t have a slider) and thus changing the last two elements of the tuple will have no effect until the axes order is changed.
Lastly, viewer.dims.point
contains the position in world coordinates (i.e., including
scale and translate transformations).
Status bar#
At the very bottom of the GUI there is a status bar that contains useful updates and tips.
On the left side of the status bar there is a message about the position of the mouse and the values of any images or the indices of any Points
that are currently hovered over, depending on which layer is selected. When there are buttons in the layer controls panel, the status bar displays information about the layer controls button you are clicking. The buttons are not available for every layer type.
The right side of the status bar contains some helpful tips depending on which layer and tools are currently selected.
Changing the viewer theme#
Currently, napari comes with light
, dark
themes for the viewer; the default is dark
. Additionally, there is the system
virtual theme that will attempt to match the viewer theme (light
or dark
) to your system theme on macOS, Windows, and some Linux. To change the preferred theme used for all viewers you can use the Preferences menu item in the File or napari menu and then select the Appearance tab. You can also change the theme
property of the current viewer by using the following code:
from skimage import data
import napari
viewer, image_layer = napari.imshow(data.astronaut(), name='astronaut')
# change the viewer theme
viewer.theme = 'light'
Show code cell source
nbscreenshot(viewer, alt_text="A napari viewer changed to light theme")
You can also change the theme using the “Toggle theme” keyboard shortcut, by default Command/Control+Shift+T
. Note that changing the theme using this shortcut will only change the current viewer theme. If you wish to make the change permanent for all viewers, make sure to also change your settings in the Appearance tab of the Preferences menu.
Adding your own custom theme isn’t too hard but it requires creating your own color palette
and rebuilding the icons. It’s also possible for plugins to contribute a theme. If people want more themes, we’re happy to add them or you can look at our contributing guidelines for more information about building the icons and add one yourself!
Custom keybinding#
napari provides a number of built-in keyboard shortcuts, which you can access and change in Preferences>Shortcuts.
Note: Preferences is under the File menu on Windows and under napari on macOS.)
One of the promises of napari is to provide a beginner friendly environment for interactive analysis. For example, we want to enable workflows where people can interact with the GUI, say, click on the centers of some objects or paint over some regions and then perform custom analysis. As a first step towards enabling custom interactivity we’ve provided support to add your own custom keybindings to the Viewer
or individual Layer
objects such that when the corresponding key gets clicked, your custom function gets executed. Depending on which object you bind your key to, your function will either get access to the state of the entire Viewer
or Layer
object.
For example, to bind a function that loops through all layers in the viewer and prints their names to your console when you press the p
key you can do the following:
from skimage import data
import napari
viewer, image_layer = napari.imshow(data.astronaut(), name='astronaut')
@viewer.bind_key('p')
def print_names(viewer):
print([layer.name for layer in viewer.layers])
By default, your key will bind to the key press event, but it is also possible to bind to the key release event by including a yield
inside your function. All code before the yield
will get executed on key press and all code after the yield
will get executed on key release. The following example will print hello
when you start to press the m
key and print goodbye
when you release it.
viewer, image_layer = napari.imshow(data.astronaut(), name='astronaut')
@viewer.bind_key('m')
def print_message(viewer):
print('hello')
yield
print('goodbye')
Keys can be bound both to the object class or a particular instance depending on if you want the keybinding to apply to all instances of the class or only one particular instance.
Currently the keybindings only work when the canvas is in focus, we are working to ensure they always work.
The ability to add custom keybindings dramatically increases what is possible within napari and we hope you take full advantage of them.
Next steps#
This tutorial has given you an overview of the functionality available on the napari viewer, including the LayerList
and some of the different layer types. To learn more about the different layer types napari supports, check out our guides on using layers.
For a more detailed introduction to layer manipulation see Layers at a glance.