ipython: %matplotlib inline doesn't work well with ipywidgets 6.0 interact

It seems that in the jupyter notebook, %matplotlib inline doesn’t play nice with the new ipywidgets 6.0 @interact anymore. As best as I can tell, the inline backend waits until the cell is completely finished before sending the display_data message with the image (see the post-execute hook registered at https://github.com/ipython/ipython/blob/7cde22957303ab53df8bd464ad5d7ed616197f31/IPython/core/pylabtools.py#L383). In ipywidgets 6.0, we’ve changed how interacts work to be more specific about the outputs we capture - we capture output messages sent during execution of the function, and display those in the interactive output. However, since the matplotlib inline backend sends its image after the function is executed, we just get a huge series of images in the output area of the cell.

import matplotlib.pyplot as plt
from ipywidgets import interact

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])

This issue is about starting the discussion to see if there’s a way for us to come up with a good solution that works well with the inline backend (which is useful) and also works well with the ipywidgets interact.

For example, if there was a way to tell the inline backend to flush its image immediately and not after a cell finished executing, that might be enough. This code works, for example (without the inline backend being enabled)

import matplotlib.pyplot as plt
from ipywidgets import interact

x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    plt.show()

CC @SylvainCorlay, @tacaswell.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 2
  • Comments: 48 (36 by maintainers)

Most upvoted comments

Hi all, I hadn’t been following the technical details of this, but have run into the problem. Regardless of implementation details I think it is extremely important that matplotlib+interact “just works” as much as possible.

Is there a version of ipywidgets and/or matplotlib I can roll back to in order to get my interactive widgets to work again?

It turns out that this is all much easier than I thought - I must have done something wrong when I tried testing this earlier. We don’t need to call flush_figures, it’s enough to just call the standard plt.show:

import matplotlib.pyplot as plt
from ipywidgets import interact

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    plt.show()

I think that’s perfectly reasonable to ask people to insert a show() command in their interact, so I’m closing this.

Here’s an even simpler example. Two cells, without the %matplotlib inline backend. Perhaps @tacaswell could shed some light on why the second plt.show doesn’t show a plot.

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1,1)
ax.plot([0,1],[0,1])
plt.show()

shows plot

ax.plot([1,0],[0,1])
plt.show()

doesn’t show anything

@jasongrout That is the expected behavior because in the first cell you create a figure (and canvas and axes) and then display it. In the second cell you then add an line to the existing axes (in the existing figure). With %matplotlib qt or ipympl (or any of the full GUIs) the plot should be update with the second line. I don’t expect this to work with inline as you only get one shot to update it (which is why I am very much not a fan of inline, it throws out all of the interactive functionality).

You can check out the ipympl package (https://github.com/matplotlib/jupyter-matplotlib), which is the matplotlib widget. ipympl is still pretty early stage, but should have a faster release schedule than matplotlib core, and earlier support for jupyterlab etc…

Yes, the mirrored output should work with the changed discussed above. Thanks for making sure!

In the long run, I think that an approach that should work better is the ipympl widgets backend to matplotlib.

However, we will need to make all of this more easily installed with the conf.d etc…

gotcha sorry for the noise.