Reputation: 600
I have a piece of Python code for an animated visualisation that stopped working recently. I have tracked the problem to an upgrade from matplotlib
3.2.2 to 3.3.2. I compared the documentations of matplotlib
3.2.2 and 3.3.2, and the main changes are the HMTLAnimationWriter and errata in the examples. I see no reason why my code should stop working.
I have raised a bug report at matplotlib
The code at the end of the post moves a line up and down in the y direction. It does that successfully with matplotlib
3.2.2, but not with 3.3.2. This is reproducible in different settings.
When I use matplotlib
3.3.2, an empty figure opens.
When I run the code from the command line (eg. python test.py
) with matplotlib
3.2.2., I get no Depreciation Warning
, or any other error messages. Similar for matplotlib
3.3.2. This has been tested with
C1: A desktop with Asrock Z370 P4, Nvidia 1050 Ti, i5-8600K.
Tested with:
C2: An Acer Spin 5 SP513-52N laptop.
Tested with:
According to a user comment:
Tested with:
In my two cases, when I upgraded the problem appeared, and when I downgraded the problem disappeared. So, my guess is that the problem lies with matplotlib
3.3.2.
An interesting point, is that if I use Pillowriter to create an animated gif, the resulting gif performs as expected in all versions of matplotlib
.
FuncAnimation()
commands are affected. This works with matplotlib 3.3.2.I am including an MCVE code that reproduces the main problem and works with matplotlib
3.2.2, but not with 3.3.2.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
def create_animated_plot(xs, niter, xlim =(-1.5, 1.5), ylim = [-3, 3]):
line_coords = np.vstack(( np.array([-1,1]), np.array([0,0]) ))
boxData = [np.array([np.zeros(niter), xs])]
fig, ax1 = plt.subplots(1,1)
line, = ax1.plot([], [], lw=2)
ax1.set_ylabel('position')
ax1.set_ylim(ylim)
ax1.set_xlim(xlim)
lines = [ax1.plot([],[],lw=2,color="black")[0]]
def init():
lines[0].set_data([],[])
return lines
def animate(i):
xs = [boxData[0][0, i]]
ys = [boxData[0][1, i]]
lines[0].set_data(line_coords[0,:]+xs[0], line_coords[1,:]+ys[0])
return lines
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=niter, interval=10, blit=True)
niter = 1001
t = np.linspace(0,10,niter)
create_animated_plot(xs =np.sin(t), niter=niter)
plt.show()
The following are images from my original code (which I reduced to the MCVE code in this post):
Please note that the MCVE code posted here just creates the upper frame of the two graphs and shows a bouncing line with matplotlib
≤3.2.2, but an empty frame with matplotlib
3.3.2.
Upvotes: 4
Views: 562
Reputation: 600
The problem was that the gc now collects more actively the references to animation objects. This is related to bug
An easy workaround is to pass out the reference like:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
def create_animated_plot(xs, niter, xlim =(-1.5, 1.5), ylim = [-3, 3]):
line_coords = np.vstack(( np.array([-1,1]), np.array([0,0]) ))
boxData = [np.array([np.zeros(niter), xs])]
fig, ax1 = plt.subplots(1,1)
line, = ax1.plot([], [], lw=2)
ax1.set_ylabel('position')
ax1.set_ylim(ylim)
ax1.set_xlim(xlim)
lines = [ax1.plot([],[],lw=2,color="black")[0]]
def init():
lines[0].set_data([],[])
return lines
def animate(i):
xs = [boxData[0][0, i]]
ys = [boxData[0][1, i]]
lines[0].set_data(line_coords[0,:]+xs[0], line_coords[1,:]+ys[0])
return lines
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=niter, interval=10, blit=True)
return anim # return anim object reference ########################################################
niter = 1001
t = np.linspace(0,10,niter)
anim = create_animated_plot(xs =np.sin(t), niter=niter) # assign to variable ###########################
plt.show()
If you have a class, you could also assign the animation object to self.
NOTE: I could not have come up with this answer without the help and patience of Mr. T
Upvotes: 2