Search code examples
pythonanimationmatplotlibz-order

Pyplot zorder lost with animation


I am trying to animate different objects in the same graph using pyplot's funcanimation.
It works almost as I expect it to, except for the order in which the different elements are displayed in. So the plot curve, text and legend are shown behind the image where they are barely seen.

Here is my (not so) minimal working example:

#! /usr/bin/python
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import random

def init():
    line_simu.set_data([], [])
    time_text.set_text('')
    imobj.set_data(np.zeros((100, 100)))
    imobj.set_zorder(0)
    time_text.set_zorder(10)
    return line_simu,  time_text, imobj

def animate(i):
    imobj.set_zorder(0)
    time_text.set_zorder(10)
    y_simu = np.linspace(0,100, 100)
    x_simu =    np.linspace(-10, 10, 100) 
    line_simu.set_data(x_simu, y_simu)

    time_text.set_text('time = %.1f' % i )

    global data
    imobj.set_data( data + np.random.random((100,1)) * 0.5 )

    return line_simu, time_text, imobj


def forceAspect(ax,aspect=1):
    im = ax.get_images()
    extent =  im[0].get_extent()
    ax.set_aspect(abs((extent[1]-extent[0])/(extent[3]-extent[2]))/aspect)


fig = plt.figure()
ax = plt.axes(xlim=(-15,15), ylim=(-110, 0) , aspect=1)

data = np.random.random((100,100)) - .5
imobj = ax.imshow( data , extent=[-15,15, -110, 0.0], origin='lower', cmap=plt.cm.gray, vmin=-2, vmax=2, alpha=.7, zorder=0, aspect=1)

line_simu, = ax.plot([], [],"r--", lw=2, markersize=4 , label = "Some curve" ,  zorder= 2 )

time_text = ax.text(-14.9, -108, '', zorder=10)

l = plt.legend(loc='lower right', prop={'size':8} )
l.set_zorder(200)

forceAspect(ax,aspect=1)

anim = animation.FuncAnimation(fig, animate, init_func=init,  frames=range( 50), interval=3000, blit=True)

plt.show()

Without animation, I can easily control the order of the different elements with set_zorder, but when the animation updates the image, this order is lost. I tried to set the zorder in the init function and again in the animate function, without success.

I am very thankful for any help on that matter.


Solution

  • To answer my own question: It seems that the order in witch the init() and animate() functions return objects controls the order in which they are displayed. Additionally those functions should return the legend object in order to include it in the animation.

    Here is my corrected code:

    #! /usr/bin/python
    import numpy as np
    from matplotlib import pyplot as plt
    from matplotlib import animation
    import random
    
    def init():
        imobj.set_data(np.zeros((100, 100)))
        line_simu.set_data([], [])
        time_text.set_text('time = 0.0')
    
        return imobj , line_simu,  time_text,  l
    
    def animate(i):
        global data
        imobj.set_data( data + np.random.random((100,1)) * 0.5 )
    
        imobj.set_zorder(0)
        y_simu = np.linspace(-100,-10, 100)
        x_simu = np.linspace(-10, 10, 100) 
        line_simu.set_data(x_simu, y_simu)
        time_text.set_text('time = %.1f' % i )
    
    
        return imobj , line_simu, time_text,  l
    
    
    def forceAspect(ax,aspect=1):
        im = ax.get_images()
        extent =  im[0].get_extent()
        ax.set_aspect(abs((extent[1]-extent[0])/(extent[3]-extent[2]))/aspect)
    
    
    
    fig = plt.figure()
    ax = plt.axes(xlim=(-15,15), ylim=(-110, 0) , aspect=1)
    
    data = np.random.random((100,100)) - .5
    imobj = ax.imshow( data , extent=[-15,15, -110, 0.0], origin='lower', cmap=plt.cm.gray, vmin=-2, vmax=2, alpha=1.0, zorder=1, aspect=1)
    
    line_simu, = ax.plot([], [],"r--", lw=2, markersize=4 , label = "Some curve" ,  zorder= 1 )
    time_text = ax.text(-14.0, -108, '', zorder=10)
    
    forceAspect(ax,aspect=1)
    
    l = plt.legend(loc='lower right', prop={'size':8} )
    
    anim = animation.FuncAnimation(fig, animate, init_func=init,  frames=range( 50), interval=500, blit=True)
    
    plt.show()