Les animations¶
La méthode after
¶
La technique pour créer des animations en Tkinter est basée sur l’usage de la méthode after
. Il s’agit d’une méthode associée à un widget et permettant d’appeler avec un certain délai (ou même périodiquement) l’exécution d’une fonction qui se charge d’animer (en déplaçant ou supprimant des objets, en en changeant les propriétés, ou la vitesse de déplacement, etc).
Voici un exemple très simplifié permettant de voir comment fonctionne et s’utilise la méthode `after`
Quand l’interface s’ouvre, on observe un rectangle rouge et au bout d’une seconde, un rectangle bleu est ajouté.
Le code corresponsant est :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from tkinter import Tk, Canvas
root = Tk()
cnv = Canvas(root, width=180, height=100)
cnv.pack()
cnv.create_rectangle(20, 20, 80, 80, fill='red', outline='')
def dessiner(c):
cnv.create_rectangle(100, 20, 100+c, 20+c, fill='blue', outline='')
cnv.after(1000, dessiner, 42)
root.mainloop()
|
Ligne 7 : le rectangle rouge visible au départ.
Lignes 9-10 : fonction qui va dessiner un carré bleu de côté
c
qui sera donné en argument à la fonction.Ligne 12 : appel de la méthode
after
en tant que méthode du canevas. L’appel prend ici 3 arguments- le premier argument
1000
qui correspond à une durée en millisecondes - le 2e argument
dessiner
qui est la fonction qui sera appelée après le délai de 1000 ms - les autres arguments (ici juste 42), les arguments, dans l’ordre qu’il faudra passer à la fonction
dessiner
donnée en 2e argument de l’appel de la méthodeafter
.
- le premier argument
On peut appeler after
depuis n’importe quel widget ou fenêtre :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from tkinter import Tk, Canvas
root = Tk()
cnv = Canvas(root, width=180, height=100)
cnv.pack()
cnv.create_rectangle(20, 20, 80, 80, fill='red', outline='')
def dessiner(c):
print(c)
cnv.create_rectangle(100, 20, 100+c, 20+c, fill='blue', outline='')
root.after(1000, dessiner, 42)
root.mainloop()
|
- Ligne 13 : appel de la méthode
after
comme une méthode de la fenêtreroot
.
La méthode after
: usage typique¶
L’usage typique de la méthode after
est qu’elle appelle à intervalle régulier (et non plus une seule fois) une fonction d’animation.
Voici un exemple. Le programme ci-dessous montre un carré bleu se déplaçant aléatoirement chaque seconde :
Le code correspondant est :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from tkinter import Tk, Canvas
from random import randint
root = Tk()
cnv = Canvas(root, width=180, height=100)
cnv.pack()
def dessiner(item=None):
cnv.delete(item)
a=randint(1,90)
b=randint(1,50)
c=50
item=cnv.create_rectangle(a, b, a+c, b+c, fill='blue', outline='')
root.after(1000, dessiner, item)
dessiner()
root.mainloop()
|
- Ligne 9-15 : fonction d’animation qui est appelée périodiquement.
- Ligne 10 : la fonction efface le carré présent sur le canvas.
- Ligne 11-14 : la fonction
dessiner
crée un carré, nommé encoreitem
, aléatoire (lignes 11-12), de 50 pixels de côté (ligne 13) et le dessine (ligne 14). - Ligne 15 : l’élément fondamental de l’animation qui permet de boucler l’animation. La méthode
after
temporise 1000 ms et fait en sorte que la fonctiondessiner
soit appelée en prenant le nouveau carré comme argument.
Illustrer after
en créant des images¶
La méthode after
permet de déclencher avec un certain délai de temps une action définie par une fonction.
Soit par exemple le code ci-dessous :
after_images.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | from tkinter import *
from random import randrange
SIDE=400
root = Tk()
cnv = Canvas(root, width=SIDE, height=SIDE)
cnv.pack()
logo = PhotoImage(file="python.gif")
def action(x, y):
cnv.create_image((x, y), image=logo)
x, y= randrange(SIDE),randrange(SIDE)
cnv.after(1000, action, x, y)
x, y= randrange(SIDE),randrange(SIDE)
cnv.after(2000, action, x, y)
x, y= randrange(SIDE),randrange(SIDE)
cnv.after(3000, action, x, y)
x, y= randrange(SIDE),randrange(SIDE)
cnv.after(4000, action, x, y)
x, y= randrange(SIDE),randrange(SIDE)
cnv.after(5000, action, x, y)
x, y= randrange(SIDE),randrange(SIDE)
cnv.after(6000, action, x, y)
root.mainloop()
|
On a défini une fonction action
qui prend deux paramètres (le nom action
n’a rien d’obligatoire).
Voici l’exécution :
Lorsque le programme est lancé, les 6 instructions cnv.after(ms, action, x, y)
vont être exécutées les unes après les autres et instantanément. Chacune de ces instructions va déclencher l’exécution de la fonction action
mais après le délai en ms indiqué par l’appel cnv.after(ms, action, x, y)
. Par exemple, une instruction telle que cnv.after(4000, action, 200, 300)
va provoquer, 4 secondes (4000 ms) après avoir été activée, l’exécution de l’appel action(200, 300)
. D’où l’effet d’animation.
La méthode after
n’est pas spécifique de Canvas : dans le code ci-dessus, on pourrait remplacer les appels cnv.after
par des appels root.after
.
Annuler la méthode after
¶
Un appel à la méthode after
renvoie un identifiant de la tâche qui va être relancée. Cet identifiant, disons id_anim
, peut être utilisé pour annuler cette tâche. Pour cela, il suffit d’appeler after_cancel(id_anim)
où after_cancel
est une méthode d’un widget.
Dans l’exemple ci-dessous, un compteur est lancé :
Un bouton permet (artificiellement) d’annuler l’animation et d’en relancer une nouvelle avec un nouveau compteur.
Le code est :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | from tkinter import Tk, Canvas, Button
from random import randrange
def anim(cpt):
global id_anim
cnv.delete("all")
cnv.create_text(SIDE//2, SIDE//2, text =int(cpt), font="Arial 200 bold")
id_anim=cnv.after(500, anim, cpt+1)
def go():
cnv.after_cancel(id_anim)
anim(1)
SIDE=400
root = Tk()
cnv = Canvas(root, width=SIDE, height=SIDE, bg='ivory')
cnv.pack()
btn=Button(root, text="Reset", command=go)
btn.pack()
id_anim=None
anim(1)
root.mainloop()
|
- Lignes 4-8 : la fonction d’animation : elle efface tout (ligne 6) et crée le nombre suivant du compteur. La foncion est relancée toutes les demi secondes (ligne 8). Le rappel provoque la création d’un identifiant (ligne 8). Pour pouvoir agir sur cet identifiant, il est placé dans une variable globale (ligne 5).
- Ligne 18 : Création d’un bouton. Lorsqu’on clique sur ce bouton, il lance la fonction
go
. - Lignes 10-12 : le clic sur le bouton entraîne l’annulation de l’animation en cours : la méthode d’annulation
after_cancel
(ligne 11) est appelée. Puis une animation est aussitôt relancée (ligne 12).
Balle rebondissante¶
Voici une animation classique :
une balle est lancée dans un rectangle et quand elle touche un mur, elle rebondit et repart en position symétrique.
Voici le code correspondant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | from tkinter import *
SIDE=400
PAD=SIDE//12
def move(dx, dy):
ulx, uly, lrx, lry = list(map(int, cvs.coords(ball)))
# bords verticaux
if ulx <=0 or lrx >=SIDE:
dx=-dx
# bords horizontaux
elif uly<=0 or lry>=SIDE:
dy=-dy
cvs.move(ball, dx, dy)
cvs.after(30, move, dx, dy)
# Création d'un canevas
w=Tk()
cvs=Canvas(w, width=SIDE, height=SIDE, highlightthickness=0,
bg="ivory")
cvs.pack(padx=PAD, pady=PAD)
# Création d'une balle
n=20
R=SIDE//n
x0=200
y0=40
ball=cvs.create_oval(x0, y0, 2*R+x0, 2*R+y0, outline='OrangeRed2',
fill='OrangeRed2')
# Lancement de l'animation
move(4, 6)
w.mainloop()
|
- Lignes 18-21 : création du rectangle (un canevas en fait)
- Lignes 24-29 : création de la balle rebondissante. La balle a une id (
ball
, ligne 28) qui sert pour détecter la position de la balle sur le canevas (ligne 7) et la déplacer (ligne 14) - Ligne 25 : le rayon de la balle qui s’ajuste en fonction de la taille du canevas pour garder des proportions correctes.
- Lignes 26-27 : le point où la balle est lâchée.
- Lignes 6-15 : la fonction d’animation
- Ligne 32 : le lancement de l’animation.
- Ligne 6 : la fonction d’animation déplace la balle toujours de la même façon : 4 pixels horizontalement, 6 pixels verticalement (cf. ligne 32).
- Ligne 15 : l’animation est relancée toutes les 30 millisecondes.
- Ligne 14 : le déplacement de la balle après rectification éventuelle si la balle a touché le mur.
- Lignes 8-13 : gestion des collisions avec les murs. Quand la balle touche un mur, le sens de son mouvement est inversé (ce qui explique pourquoi
dx
oudy
est changé en son opposé aux lignes 10 et 13) - Ligne 7 : la balle a une id (créée ligne 28). Grâce à cette id, le canevas est en mesure de fournir les quatre coins du carré qui encadre la balle. C’est ainsi que l’on sait si la balle est en train de rebondir ou pas (lignes 9 et 12).
Pour modifier la vitesse de la balle, on peut jouer sur :
- le délai de rafraîchissement (ligne 15)
- l’amplitude de déplacement (ligne 32).
Certains choix de ces deux paramètres peuvent produire des animations disgrâcieuses.
Effet de fading¶
Un effet de fading (atténuation progressive d’une couleur) est obtenu en faisant une animation.
On passe graduellement (ici en deux secondes et 10 étapes) d’un carré jaune à un carré blanc :
Le code correspondant est :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | from tkinter import *
SIDE=400
root = Tk()
cnv = Canvas(root, width=SIDE, height=SIDE,
bg='light blue')
cnv.pack()
def fading_color(r,g,b, N):
return (255-r)//N,(255-g)//N, (255-b)//N
def fading(rect, r, g, b, N):
if N<=0:
return
s=cnv.itemcget(rect,"fill")[1:]
R,G,B=[int(''.join(u),base=16) for u in zip(s[0::2],s[1::2])]
rr=str(hex(min(R+r, 255)))[2:]
gg=str(hex(min(G+g, 255)))[2:]
bb=str(hex(min(B+b, 255)))[2:]
color="#"+str(rr+gg+bb)
cnv.delete(rect)
rect=cnv.create_rectangle(100, 100, 300, 300,
fill=color, outline='')
cnv.after(200, fading, rect, r, g, b, N-1)
rect= cnv.create_rectangle(100, 100, 300, 300,
fill='#f0e68c', outline='')
r, g, b=fading_color(0xf0,0xe6, 0x8c, 10)
fading(rect, r, g, b, 10)
|
- Lignes 13-23 : la fonction d’animation.
- Ligne 30 : on calcule au préalalble trois tranches
r
,g
etb
de composantes RGB que l’on va additionner (lignes 18-20) à la couleur courante à chaque étape de l’animation. - Ligne 25 : l’animation est relancée tous les 200 ms.
- Ligne 16 : on lit les couleurs courantes du rectangle en accédant aux options de l’item grâce à son id (ici
rect
) qui est un paramètre de la fonctionfading
. - Ligne 22-24 : on efface le carré courant et on le remplace par le carré avec la nouvelle couleur.