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
sinceupdate
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 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)
.