Skip to content
Advertisement

Plot don’t refresh when slider updated matplotlib

I can’t figure out why the plot don’t refresh when the slider is updated. I’m using Jupiter notebook and I choose the backend with ‘nbAgg’ parameter.

Initialization code :

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import use as m_use
from matplotlib.widgets import Slider

m_use('nbAgg')
x1 = np.random.normal(-2.5, 1, 10000)
x2 = np.random.gamma(2, 1.5, 10000)
x3 = np.random.exponential(2, 10000)+7
x4 = np.random.uniform(14,20, 10000)
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2,figsize=(7, 5))
plt.subplots_adjust(left=0.1,right=.8)

There is a animation which lunch this function :

def updateData(curr,divider=7):
    global ax1, ax2, ax3, ax4
    for ax in (ax1, ax2, ax3, ax4):
        ax.cla()
    if curr <= 679 :
        my_b = int(np.around(curr/divider)+3)
    else : my_b = 100
    
    multi = 100
    ax1.hist(x1[:curr*multi],bins=my_b)
    ax2.hist(x2[:curr*multi],bins=my_b)
    ax3.hist(x3[:curr*multi],bins=my_b)
    ax4.hist(x4[:curr*multi],bins=my_b)
    fig.suptitle('Frame {} on 100'.format((curr+1)))
    return None

The animation :

simulation = animation.FuncAnimation(fig=fig, func=updateData, frames=10,
                                     blit=False, interval=1, repeat=False)

Here the slider which stuck me :

slider_ax = plt.axes([.9, 0.45, 0.01, 0.3])
slider = Slider(ax=slider_ax,label='Divider nfor bins',valmin=1,valmax=15, valinit=7, orientation='vertical',valfmt='%.0f',track_color='black',facecolor='red',
                  handle_style={'facecolor':'k','edgecolor':'#86a2cf','size':20})
def update():
    
    anim_2 = animation.FuncAnimation(fig=fig, func=updateData, frames=20,
                                     blit=False, interval=1, repeat=False)    

This below function don’t work as expected :

slider.on_changed(update)

simulation = animation.FuncAnimation(fig=fig, func=updateData, frames=10,
                                     blit=False, interval=1, repeat=False)
plt.show()

Advertisement

Answer

You can try the following:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.widgets import Slider
from ipywidgets import interact
import ipywidgets as widget

%matplotlib widget

x1 = np.random.normal(-2.5, 1, 10000)
x2 = np.random.gamma(2, 1.5, 10000)
x3 = np.random.exponential(2, 10000) + 7
x4 = np.random.uniform(14, 20, 10000)
(fig, ((ax1, ax2), (ax3, ax4))) = plt.subplots(2, 2, figsize=(7, 5))
plt.subplots_adjust(left=0.1, right=.8)

divider = 7
def updateData(curr):
    global ax1, ax2, ax3, ax4, divider
    for ax in (ax1, ax2, ax3, ax4):
        ax.cla()
    if (curr <= 679):
        my_b = int(np.around(curr / divider) + 3)
    else:
        my_b = 100

    multi = 100
    ax1.hist(x1[:curr * multi], bins=my_b)
    ax2.hist(x2[:curr * multi], bins=my_b)
    ax3.hist(x3[:curr * multi], bins=my_b)
    ax4.hist(x4[:curr * multi], bins=my_b)
    fig.suptitle('Frame {} on 100'.format(curr + 1))
    plt.tight_layout()

def update(val):
    global divider
    divider = val
    anim_2 = animation.FuncAnimation(fig=fig, func=updateData, frames=20, blit=False, interval=1, repeat=False)
    fig.canvas.draw()

slider = interact(update, val=widget.IntSlider(min=1, max=15, value=7))

simulation = animation.FuncAnimation(fig=fig, func=updateData, frames=10, blit=False, interval=1,repeat=False)

display(slider)

Few things to note:

  • To plot interactive figures in Jupyter Notebook with matplotlib, you will need to install ipympl after which you can plot interactive Matplotlib figures in Jupyter Notebook using the magic function %matplotlib widget.

  • To update the figure once animation is stored in a variable, you’ll need to call fig.canvas.draw() to redraw the figure.

  • Your implementation wasn’t using (updated) slider value as an argument for the function update since update didn’t require any argument to work. However, widgets are often use to call function with argument(s). For example, check how interact provides an interface in Jupyter Notebook to call a same function with different values of arguments.

    Given this, I modified the update function which takes slider value to update the value of global variable divider.

This means, when calling a function updateData using FuncAnimation with frames as an integer, updateData will be invoked repeatedly with values from range(frames). This means, on every invocation of updateData by FuncAnimation, curr will take a value from range(frames).

Advertisement