updating a matplotlib plot with `observe` without generating a extra figures in a tab widget

2,904 views
Skip to first unread message

Matt Malej

unread,
Aug 24, 2016, 10:36:57 PM8/24/16
to Project Jupyter
I'm looking to update matplotlib inline plots interactively that are changed inside of tab widget without generating a series of figures one below the other (see images below -- a second,third... figure is generated every time slider changes). Obviously this can be done without the tab environment via `interact`, but I can't seem to figure out how in this short example here can I change the plot in-place (on same figure) with the Slider widget. Thanks in advance for any advice.
 
%matplotlib inline
import matplotlib.pyplot as plt
from IPython.display import display
import ipywidgets as widgets
from ipywidgets import interact, Button, HBox, VBox
import numpy as np

# plot func
t = np.arange(0.0, 1.0, 0.001)
def sinePlot(f):
    x = np.linspace(0.0, 2.0, 1000)
    plt.plot(x,np.sin(x*np.pi*t*f))

words = "one two three four".split(' ')
items = [Button(description=w) for w in words]
myBox = HBox([VBox([items[0], items[1]]), VBox([items[2], items[3]])])

lst = ['P0', 'P1', 'P2']
mySlider = widgets.FloatSlider(value=0.5, min=0.1, max=1, step=0.1, description=('Slider'))
children = [widgets.Text(description=lst[0], width='250px'), myBox, mySlider]

tab = widgets.Tab(children=children)
[tab.set_title(num, name) for num, name in enumerate(lst)]

# initial plot
sinePlot(f=1.0)
display(tab)

# on slider change
def on_slider_change(change):
    #print(change['new'])
    sinePlot(change['new'])
    
mySlider.observe(on_slider_change, names='value')



Fernando Perez

unread,
Aug 25, 2016, 9:13:51 PM8/25/16
to Project Jupyter
On Wed, Aug 24, 2016 at 7:36 PM, Matt Malej <matt....@gmail.com> wrote:
I'm looking to update matplotlib inline plots interactively that are changed inside of tab widget without generating a series of figures one below the other (see images below -- a second,third... figure is generated every time slider changes). Obviously this can be done without the tab environment via `interact`, but I can't seem to figure out how in this short example here can I change the plot in-place (on same figure) with the Slider widget. Thanks in advance for any advice.

@interact manages clearing the output area for you, where as bare sliders don't.  You need to use clear_output, from IPython.display.  Here's a small self-contained version of your example, removing the tabs/buttons part which doesn't affect this problem (the issue is visible just with one slider).

I've also modified the plotting code to avoid re-creating the figure object over and over, and instead reusing the axis object where the plotting actually occurs.  This will save you quite a bit of unnecessary overhead on each call.

If you really need to optimize the refresh, you can go further by grabbing the raw line object from matplotlib and updating its data via set_data, but I won't do that here to avoid cluttering the example.

Cheers

f


```
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

from IPython.display import display, clear_output
import ipywidgets as widgets

# plot func
fig, ax = plt.subplots()
plt.close(fig)

t = np.arange(0.0, 1.0, 0.001)
x = np.linspace(0.0, 2.0, 1000)
s = x*np.pi*t

def sinePlot(f):
    ax.clear()
    ax.plot(x, np.sin(s*f))
    display(fig)

mySlider = widgets.FloatSlider(value=0.5, min=0.1, max=1, step=0.1, description=('Slider'))

# initial plot
sinePlot(f=1.0)

# on slider change
def on_slider_change(change):
    clear_output(wait=True)
    sinePlot(change['new'])
    
mySlider.observe(on_slider_change, names='value')
display(mySlider)
```


--
Fernando Perez (@fperez_org; http://fperez.org)
fperez.net-at-gmail: mailing lists only (I ignore this when swamped!)
fernando.perez-at-berkeley: contact me here for any direct mail

Matt M

unread,
Aug 26, 2016, 8:25:41 PM8/26/16
to Project Jupyter
Thanks, Fernando! It worked like a charm.

oscar6echo

unread,
Aug 27, 2016, 3:10:28 PM8/27/16
to Project Jupyter
Building on Matt question and Fernando's answer, I have added a player synced with the slider and update the graph from the slider. Surprisingly the slider->graph link works, the player->slider link works, but NOT the player->graph.

I put a print statement in the `on_player_change` function and it does not print anything.
So it seems that observe does not work for the play widget.
But the link with the slider does work, so I don't understand why the slider changing does not trigger `on_slider_change`.

Any idea with ?
Has somebody successfully manage to observe a player ? 

````
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

from IPython.display import display, clear_output
import ipywidgets as widgets

fig, ax = plt.subplots()
plt.close(fig)

t = np.arange(0.0, 1.0, 0.001)
x = np.linspace(0.0, 2.0, 1000)
s = x*np.pi*t

def sinePlot(f):
    ax.clear()
    ax.plot(x, np.sin(s*f))
    ax.set_ylim([-1, 1])
    ax.set_title('slider/100 = {:.2f}'.format(f))
    display(fig)

def g(x):
    return x/100.0
    

slider = widgets.IntSlider(value=50, min=2, max=100, step=2, description=('slider'))
play = widgets.Play(value=50)
widgets.jslink((play, 'value'), (slider, 'value'))

def on_slider_change(change):
    clear_output(wait=True)
    sinePlot(g(change['new']))
slider.observe(on_slider_change, names='value')

def on_player_change(change):
    print(change['new'])
play.observe(on_player_change, names='value')

box = widgets.VBox([play, slider])

sinePlot(f=g(slider.value))
display(box)
````

Reply all
Reply to author
Forward
0 new messages