Search code examples
pythonmatplotlibmatplotlib-animation

How to remove/update old animation lines


Here my code stripped down to its bare minimal to correctly reproduce my problem.

import matplotlib.animation as animation
import os
import matplotlib.pyplot as plt
import numpy as np
import os.path

f, ((ax1, ax2)) = plt.subplots(1, 2, figsize=(20,10))

def animate(i):
    chosenEnergy = (0.0 + (i-1)*(0.02))
    chosenEnergyLine = ax2.axvline(float(chosenEnergy),0,1, linestyle='dashed')
    return chosenEnergyLine,

def init():
    chosenEnergyLine = ax2.axvline(0.0,0,1, linestyle='dashed')
    return chosenEnergyLine,

ani = animation.FuncAnimation(f, animate, np.arange(1,nE), init_func=init,
interval=25, blit=False, repeat = False)

plt.rcParams['animation.ffmpeg_path'] = '/opt/local/bin/ffmpeg'
FFwriter = animation.FFMpegWriter()

ani.save('basic_animation.mp4', writer = FFwriter, fps=30, extra_args=['-vcodec', 'libx264'])
print "finished"

The problem is that I want to new vertical line (at a different energy) to replace the old line. Instead, the final frame shows all lines.

I found a similar question (matplotlib circle, animation, how to remove old circle in animation), but does not seem to apply for my case.

Even if a similar function as the one used in the answer to this similar question (set_radius) exists for xvline, I'd prefer not to use it. In my other subplot (ax1), I have a scatter plot that will have to be updated each time. Is there a general way to clear the plot before the next time step?

I also didn't notice any change when using blit=False or blit=True.

Any suggestion on how to proceed?


Solution

  • Each time you call animate you're drawing a new line. You should either delete the old lines, or move your line with set_xdata instead of drawing a new one.

    def animate(i,chosenEnergyLine):
        chosenEnergy = (0.0 + (i-1)*(0.02))
        chosenEnergyLine.set_xdata([chosenEnergy, chosenEnergy])
        return chosenEnergyLine,
    
    chosenEnergyLine = ax2.axvline(0.0,0,1, linestyle='dashed')
    ani = animation.FuncAnimation(f, animate, np.arange(1,10),
           fargs=(chosenEnergyLine,), interval=25, blit=False, repeat = False)
    

    UPDATE: That's because you're trying to delete the same global chosenEnergyLine multiple times (FuncAnimation catches the return value of animate but it does not update the global chosenEnergyLine with it). The solution is to use kind of a static variable in animate to keep track of the latest chosenEnergyLine

    def animate(i):
        chosenEnergy = (0.0 + (i-1)*(0.02))
        animate.chosenEnergyLine.remove()
        animate.chosenEnergyLine = ax2.axvline(float(chosenEnergy),0,1, linestyle='dashed')
        return animate.chosenEnergyLine,
    
    animate.chosenEnergyLine = ax2.axvline(0.0,0,1, linestyle='dashed')
    
    ani = animation.FuncAnimation(f, animate, np.arange(1,10),
                    interval=25, blit=False, repeat = False)