0

I want to create an animation with matplotlib, with multiple lines, not linked.

I can create this by :

def animate(i) :
    a = []
    a.append((x1,x2))
    a.append((y1,y2))
    a.append('b')
    a.append((x3,x4))
    a.append((y3,y4))
    a.append('r')
    a.append((x5,x6))
    a.append((y5,y6))
    a.append('g')
    a = plt.plot(*a)
    return a
ani = animation.FuncAnimation(fig, animate, frames = 10, blit = True, interval = 50, save_count = 50, repeat = False) 

With this code, I can't set line's linewidth.

I've tried to add plt.plot directly to a, but I have an error with zorder

What can I do ?

Thanks !

PS : I apologize for my poor English...

Edit : my code is below

import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation # pour l'animation
import matplotlib.lines as lines

nombre_etapes = 0

nombre_disques = 4

etapes = []
#on crée l'état initial : tous les disques sur le bâton 0
etapes.append([[i for i in range(nombre_disques, 0, -1)], [], []])
print("état initial : ")
print(etapes)

mouvements = [None]
def hanoi(n, origine, destination, intermediaire) :
    global nombre_etapes
    #global etapes
    if n > 0 :        
        hanoi(n - 1, origine, intermediaire, destination)
        nombre_etapes += 1
        #on crée le nouvel état        
        etat_actuel = [x for x in etapes[-1]]
        #print("état actuel avant mouvement", etat_actuel)
        disque_a_bouger = etat_actuel[origine].pop()
        #print("disque qui bouge : {}".format(disque_a_bouger))
        etat_actuel[destination].append(disque_a_bouger)
        #print("état actuel après mouvement", etat_actuel)
        etapes.append(etat_actuel)
        #print("etapes : ")
        #print(etapes)
        print(str(origine) + " --> " + str(destination))
        mouvements.append([origine, destination])
        hanoi(n - 1, intermediaire, destination, origine)
        nombre_etapes += 1

#longueurs pour dessin
rayon = 10
ecart_vertical = 0.5
epaisseur_disque = 0.5
epaisseur_baton = 2

hanoi(nombre_disques,0,2,1)
#print(mouvements)

etat = [[i for i in range(nombre_disques, 0, -1)], [], []]

def dessine_batons() :
    batons = []
    for idBaton in range(3) :
        abscisse = (2 * idBaton + 1) * nombre_disques * rayon + 2 * idBaton * rayon
        ordonnee_1 = 0
        ordonnee_2 = 4 * ecart_vertical + epaisseur_disque * nombre_disques
        batons.append((abscisse, abscisse))
        batons.append((ordonnee_1, ordonnee_2))
        batons.append('b')
    #print(batons)
    batons = plt.plot(*batons)
    return batons



fig, ax = plt.subplots()
abscisse = 3 * nombre_disques * rayon + 2 * rayon
ordonnee = ecart_vertical
disques = [[]]

couleurs = ["", "red", "blue", "grey", "green", "black", "cyan", "magenta", "crimson", "pink", "orange"]
for idDisque in range(nombre_disques, 0, -1) :
    abscisses = [abscisse - idDisque * rayon, abscisse + idDisque * rayon]
    ordonnees = [ordonnee, ordonnee]
    ordonnee += ecart_vertical + epaisseur_disque
    disque, = ax.plot(abscisses, ordonnees, c = couleurs[idDisque], linewidth = 20, zorder = 1)
    disques.append(disque)




def animate_5(idEtape, disques) :
    if idEtape != 0 :         
        #on récupère le mouvement
        mouvement = mouvements[idEtape]
        origine, destination = mouvement
        disque_qui_bouge = etat[origine].pop()
        etat[destination].append(disque_qui_bouge)
    for idBaton in range(3) :
        abscisse = (2 * idBaton + 1) * nombre_disques * rayon + 2 * idBaton * rayon
        ordonnee = ecart_vertical
        for disque in etat[idBaton] :
            abscisses = [abscisse - disque * rayon, abscisse + disque * rayon]
            ordonnees = [ordonnee, ordonnee]
            disques[disque].set_data(abscisses, ordonnees)
            disques[disque].set_zorder(disque)
            ordonnee += ecart_vertical + epaisseur_disque
    plt.pause(0.1)
    return disques



plt.axis([-10, 5 * nombre_disques * rayon + 4 * rayon + nombre_disques * rayon + 5, 0, 4 * ecart_vertical + epaisseur_disque * nombre_disques + 5])

ani = animation.FuncAnimation(fig, animate_5, init_func = dessine_batons, fargs = [disques], frames = len(mouvements), blit = False, interval = 50, save_count = 50, repeat = False) 


plt.show()

'

1 Answers1

1

Only three steps to follow:

  • Create some artists at the initialisation stage
  • Update their coordinates in the update function
  • Don't forget to return a list of updated artists.

fargs: tuple or None, optional

Additional arguments to pass to each call to func.

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

plt.rcParams['lines.linewidth'] = 5

x = np.linspace(0, 7.5, 100)
y1 = np.sin(x)
y2 = np.sin(x+0.5)
y3 = [0] * len(x)
ys = [y1, y2, y3]

fig, ax = plt.subplots()

line1, = ax.plot(x, y1, label='zorder=2', zorder=2, color='orange') #bottom
line2, = ax.plot(x, y2, label='zorder=4', zorder=4, color='blue')
line3, = ax.plot(x, y3, label='zorder=3', zorder=3, color='lightgrey', linewidth=8)
lines = [line1, line2, line3]

def update(num, x, ys, lines):
    for i in range(len(ys)):
        lines[i].set_data(x[:num], ys[i][:num])

    if num > len(x)/2:
        line1.set_linewidth(10)
        line1.set_zorder(5)       #upper
        line2.set_zorder(1)       #bottom

    print(line1.get_zorder())

    return lines


ani = animation.FuncAnimation(fig, func=update, frames=len(x), fargs=[x, ys, lines],
                            blit = True, interval = 50, save_count = 50, repeat = False)

plt.show()

Reference:


According to matplotlib.animation.FuncAnimation

If blit == True, func must return an iterable of all artists that were modified or created. This information is used by the blitting algorithm to determine which parts of the figure have to be updated.

The return value is unused if blit == False and may be omitted in that case.

Your animate_5() returns disques at last. You init disques with [[]]. Empty list is not artists. You should modify the logical to get rid of it.

Ynjxsjmh
  • 28,441
  • 6
  • 34
  • 52
  • Thanks for your answer. Unfortunately, I dit not well explain what I want. I have several segments which extremities must be calculated at every frame. For example, a red segment goes to right, and a blue one goes down. I tried to adapt your code, but I have the error : AttributeError: 'list' object has no attribute 'get_zorder' – rognntudjuu Aug 07 '20 at 19:58
  • @rognntudjuu Since I don't know how you call `get_zorder`, so I just give you an example how to call it and set linewidth. – Ynjxsjmh Aug 08 '20 at 02:45
  • I've edited my post to add my code. It works with blit = False, but I have an error with blit = True : 'AttributeError: 'list' object has no attribute 'get_zorder'' – rognntudjuu Aug 08 '20 at 19:11
  • @rognntudjuu Could you initilize your variables properly, so that I could you where you got the error. – Ynjxsjmh Aug 09 '20 at 02:03
  • I've added my all code I wanted to put this code on my Numworks, before I Checked if it supports animation... but I want to know how to do – rognntudjuu Aug 11 '20 at 18:12
  • @rognntudjuu I edited my answer to figure out where you're wrong when `blit = True`. – Ynjxsjmh Aug 12 '20 at 03:43