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
ipymplafter 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
updatesinceupdatedidn’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
updatefunction which takes slider value to update the value of global variabledivider.
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).