Interactive plot with ipyvidgets and matplotlib on binder produces static images

Question:

I’m trying to share a github repo on binder. Locally, my interactive plot using matplotlib and @interact works ok. On binder it works half way. Same code adds static images to the cell output in binder notebook when slider value changes.

Question: how to fix binder behavior and make an interactive plot?

git repository https://github.com/queezz/Complex_Numbers

My notebook looks like this:

%pylab inline
from ipywidgets import interact, widgets
x = np.linspace(0,np.pi,100)
@interact
def plot_interactive(a=widgets.FloatSlider(min=1, max=10, val=1)):
    plot(x,np.sin(x*a))
    gca().set_aspect('equal')
    ylim(-1.1,1.1)

Screenshot from binder:
enter image description here

Asked By: queezz

||

Answers:

Found a good working example:
https://github.com/Kapernikov/ipywidgets-tutorial

The gist of it is to use %matplotlib widget and @widgets.interact.

It seems that usage of %pylab inline is discouraged now, see this git issue.

I copy a part of the code from the tutorial which produces the same result as wanted in the question.

%matplotlib widget
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
# set up plot
fig, ax = plt.subplots(figsize=(6, 4))
ax.set_ylim([-4, 4])
ax.grid(True)

# generate x values
x = np.linspace(0, 2 * np.pi, 300)


def my_sine(x, w, amp, phi):
    """
    Return a sine for x with angular frequeny w and amplitude amp.
    """
    return amp*np.sin(w * (x-phi))


@widgets.interact(w=(0, 10, 1), amp=(0, 4, .1), phi=(0, 2*np.pi+0.01, 0.01))
def update(w = 1.0, amp=1, phi=0):
    """Remove old lines from plot and plot new one"""
    [l.remove() for l in ax.lines]
    ax.plot(x, my_sine(x, w, amp, phi), color='C0')
Answered By: queezz

I wasn’t able to get @queez’s answer that uses ‘interact’ to work today (later updated in early 2023 is below); however, the ipywidgets documentation presently includes a matplotlib example, which uses ‘interactive’, that I was able to take and adapt @qqueezz’s solution to get it working. This seems to be a much more streamlined route to make an interactive plot.

#%matplotlib inline # from the example in the documentation. but doesn't seem necessary in current JupyterLab 3.1.11 or the classic notebook available now https://github.com/fomightez/communication_voila
from ipywidgets import interactive
import matplotlib.pyplot as plt
import numpy as np

def my_sine(x, w, amp, phi):
    """
    Return a sine for x with angular frequency w and amplitude amp.
    """
    return amp*np.sin(w * (x-phi))

def f( w, amp, phi):
    plt.figure(2)
    x = np.linspace(0, 2 * np.pi, 300)
    plt.plot(x, my_sine(x, w, amp, phi), color='C0')
    #plt.ylim(-5, 5)
    plt.grid(True) #optional grid
    plt.show()

interactive_plot = interactive(f, w=(0, 10, 1), amp=(0, 4, .1), phi=(0, 2*np.pi+0.01, 0.01))
#output = interactive_plot.children[-1]
#output.layout.height = '450px'
interactive_plot

You can go to the repo here, launch a session by choosing the launch binder badge to the right of ‘Start with the matplotlib & widget demo as a notebook’ under ‘Direct links to start out in notebook mode:’.
Or click here to launch directly into that notebook via MyBinder.


UPDATE:
Later (early 2023), I found queez’s code from Kapernikov: Ipywidgets with matplotlib did work with interact in JupyterLab using ipympl.
Not following yet what changed; however, wanted to note.

Answered By: Wayne