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()
CodePudding user response:
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)
.