Wait for napari window to close to advance?

I use napari to define ROIs for a number of different image stacks. To do this, I use a Jupyter notebook with a for loop that runs through all of my stacks. This loop relies on an imported function that calls napari and then extracts shapes from a layer in the viewer. Here’s the function:

def getROIs(stack, roiFN, oldROIs, oldType):
    """ Use napari to get ROIs from a stack, using a given ROI function
    """
    
    # Load the stack in napari
    viewer = napari.Viewer()
    viewer.add_image(stack)
    if len(oldROIs) > 0:
        viewer.add_shapes(oldROIs, shape_type=oldType, name = 'Shapes')
    napari.run()
      
    # Use the ROIs that were drawn in napari to get image masks
    [napOut, allROIs, allMasks] = roiFN(viewer, stack)
    
    return [napOut, allROIs, allMasks]

I originally ran this function on my Windows machine, using napari 0.4.4 and with napari.gui_qt() at the beginning instead of napari.run() at the end. In that case, my code waited until I defined my ROIs and closed the napari window to advance to the [napOut, allROIs, allMasks] = roiFN(viewer, stack) line. I am now trying to run it on my Mac using napari 0.4.7, and it automatically advances to the [napOut, allROIs, allMasks] = roiFN(viewer, stack) line without waiting (regardless of whether I use napari.run() or with naprit.gui_qt()). What am I missing?

1 Like

By default, in IPython and Jupyter notebook, napari will now use the “interactive” gui event loop (i.e. the equivalent of using the %gui qt magic). In that context, napari.run() will do essentially nothing. You can read more about it in the docs here… For your case, if you’d like control the event loop and have napari.run block until all windows are closed, while still working in a notebook, then you’ll want to use the first “tip” in the docs linked above, and turn off the ipy_interactive setting:

import napari
from napari.utils.settings import SETTINGS

# if ipy_interactive is false, each viewer will wait before continuing
# otherwise you'll immediately get 4 viewers.
SETTINGS.application.ipy_interactive = False
for i in range(4):
    viewer = napari.Viewer(title=f'viewer {i}')
    napari.run()
2 Likes

@talley it’s a bit messy to have to do a setting for this. In mpl-land, you can do plt.show(block=True) to force the console to wait until the UI is closed. What do you think about napari.run(block=True)?

one of the main “problems” we have when making any comparisons to mpl, is the fact that viewer.show is True by default (discussed briefly here). This was presumably done in order to avoid having users do viewer.show(), but it does provide challenges for us as now the showing of the viewer is strictly linked to the event loop itself.

To work around that, the pr that added this did originally expose this as a parameter in the viewer constructor, but it was decided that we didn’t want those parameters in viewer.__init__ :slight_smile:

we can probably make napari.run(block=True) work… but this is all a bit odd

1 Like