Aller au contenu

Introduction:

Ce que nous savons faire...

Dans un travail précédent, nous avons appris à:

  • construire une interface graphique grâce aux possibilités du module Tkinter ;
  • déclencher une action à partir d'un bouton de commande ;
  • transporter l’information donnée par un widget de choix dans une variable de contrôle dont on peut récupérer la valeur à tout moment ;
  • interagir avec l’utilisateur en récupérant les coordonnées du clic de souris dans le canvas.
  • piloter un objet au clavier

Le paragraphe suivant illustre des méthodes classiques pour créer une animation dans un canvas...

Exo

Récupérer le code suivant qui définit l'interface sur laquelle nous allons travailler.

Code à copier
🐍 Script Python
from tkinter import *
from random import randint
###################################################VARIABLES  ###############################
ListeCouleurs = [ 'green', 'red', 'black', 'blue', 'maroon', 'orange']
#############################################################################################
######################################## DECLARATION DES FONCTIONS ##########################
def cercle():
    x, y = randint(0, 800), randint(0, 580)
    r = valeur.get()
    Can.create_oval(x - r, y - r, x + r, y + r, fill = coul.get())
    Can.after(100, cercle)

def efface():
    Can.delete(ALL)

##############################################################################################
##############################################################################################
############### CONSTRUCTION DES OBJETS DANS LA FEN ##########################################
##############################################################################################
fen = Tk()
fen.title( "Animation dans un canvas")
fen.geometry('1000x600+100+100')
fen.resizable(False, False)
#widthxheight+bordecran
##############################################################################################
ZoneDessin = Frame(fen, borderwidth = 2, relief = RIDGE)
ZoneDessin.grid(row = 0, column = 0)
##############################################################################################
Can = Canvas(ZoneDessin, bg = 'grey', width = 800, height = 580)
Can.grid()
##############################################################################################
ZoneOptions = Frame(fen, borderwidth = 2,width = 200, height = 580)
ZoneOptions.grid(row = 0, column = 1)
ZoneOptions.grid_propagate(0)
##############################################################################################
BouDebut = Button ( ZoneOptions,text = 'Débuter la construction', command = fen.destroy)
BouDebut.grid(row =0, pady = 20,columnspan = 2)
BouFin = Button ( ZoneOptions,text = 'Stopper la construction', command = fen.destroy)
BouFin.grid(row =1, pady = 20,columnspan = 2)
BouEfface = Button ( ZoneOptions,text = 'Effacer', command = fen.destroy)
BouEfface.grid(row =2, pady = 20,columnspan = 2)

##############################################################################################
valeur = IntVar(ZoneOptions)
valeur.set(5)
EchelleRayon = Scale(ZoneOptions, orient = 'horizontal', from_=0,
    to = 100, resolution = 1, tickinterval = 10, length = 190,
    label = 'Dimension du cercle ou du carre', variable = valeur)
EchelleRayon.grid(row = 3, columnspan = 2)
##############################################################################################
coul = StringVar(ZoneOptions)
coul.set(ListeCouleurs[0])
ChoixCoul = OptionMenu(ZoneOptions, coul, *ListeCouleurs)
ChoixCoul.grid(row = 4, column = 0, pady = 20)
##############################################################################################
##############################################################################################
fen.mainloop()

Qu'est ce qu'une animation?

Le mouvement d’un objet dans un canvas (zone de dessin ) est une animation : cet objet étant défini par ses coordonnées, il suffit alors de faire évoluer ces valeurs pour voir bouger l’objet. C’est ce choix que nous adopterons dans un second temps...

Mais avant cela, on définit un appel récursif d’une fonction pour la construction d’un objet dans le canvas.

Créer et contrôler l'animation:

La fonction after(...).

Voici le code d'une nouvelle fonction cercle:

🐍 Script Python
def cercle():
    x, y = randint(0, 800), randint(0, 580)
    r = valeur.get()
    Can.create_oval(x - r, y - r, x + r, y + r, fill = coul.get())
    Can.after(100, cercle)#Appel recursif de la fonction cercle

Cette fonction est dite récursive car elle s'appelle elle-même toutes les 100ms. Donc si aucun événement ne l'arrête, elle boucle à l'infini!

Exo

Associer cette fonction au bouton BouDebut et tester!

Il ne suffit pas de lancer une animation, il faut aussi la maîtriser : en particulier,

  • comment arrêter l'animation ?
  • comment reprendre l'animation quand on le souhaite ?

En effet, la fonction after crée un appel récursif et perpétuel de la fonction cercle() ; il n’y donc aucune raison que la construction s’achève...

Une première astuce avec un compteur

Pour répondre à notre problématique, nous allons utiliser encore des variables globales. Il faut comprendre qu’une variable crée au sein d’une fonction n’existe que dans cette fonction ; on dit que la variable est locale. En particulier, elle n’est donc pas visible en dehors de la fonction.

Info

Une variable globale est déclarée dans le corps de programme et est initialisée. Pour qu’une fonction puisse modifier son état, il faut la déclarer globale dans la fonction comme le montre l’exemple suivant:

🐍 Script Python
compteur = 0 #je suis une variable globale
#################### DECLARATION DES FONCTIONS ###################
def cercle():
    global compteur #variable définie en dehors de la fonction
    x, y = randint(0, 800), randint(0, 580)
    r = valeur.get()
    if compteur < 20:
        compteur += 1
        Can.create_oval(x - r, y - r, x + r, y + r, fill = coul.get())
        Can.after(1000, cercle)

Exo

  1. Modifier le code de l'interface en y incluant les instructions ci-dessus.
  2. Combien de cercles seront construits? Modifier le code pour construire 100 cercles en 50ms.

Contrôle par l'utilisateur

Nous allons utiliser le bouton de commande qui stoppe l’animation. Pour cela on utilise traditionnellement une variable globale, nommée flag, qui contrôle le déroulement de l’animation selon le principe suivant :

  • la variable est initialisée à 0;
  • l'animation n'est possible que si flag à pour valeur 1.

Exo

  1. Supprimer la variable compteur ainsi que la fonction cercle précédente.
  2. Copier le code ci-dessous.
  3. Associer à chaque bouton, la commande qu'il permet d'activer.
🐍 Script Python
flag = 0 #je suis globale
######################################## DECLARATION DES FONCTIONS ##########################
def demarrer():
    global flag
    if flag == 0:#comparaison
        flag = 1
        cercle()

def stopper():
    global flag
    flag = 0

def cercle():
    global flag
    x, y = randint(0,800),randint(0,580)
    r = valeur.get()
    Can.create_oval(x - r, y - r, x + r, y + r, fill = coul.get())
    if flag == 1: #si flag passe à 0, l'animation s'arrête
        Can.after(100,cercle)

Une animation simple avec la méthode coords.

Nous allons maintenant pour finir, faire évoluer une balle rouge dans le canvas à l'aide de la méthode coordsque nous avons déjà rencontrée dans un travail précédent.

Info

La méthode coords permet de rédéfinir les coordonnées d'un objet (déjà créé) dans un canvas.

Exo

Modifiez le code précédent en y ajoutant les éléments suivants.

🐍 Script Python
flag = 0 #je suis globale
x, y = 0, randint(0, 580) #nous aussi
######################################## DECLARATION DES FONCTIONS ##########################
def demarrer():
    global flag, balle
    r = valeur.get()
    if flag == 0:#comparaison
        flag = 1
        balle = Can.create_oval(x - r, y - r, x + r, y + r, fill = coul.get())
        bouge()

def stopper():
    global flag, x, y
    flag = 0
    x = 0
    y = randint(0, 580)


def bouge():
    global flag, x, y, balle
    r = valeur.get()
    if flag == 1: #si flag passe à 0, l'animation s'arrête
        x = x + 2
        Can.coords(balle, x - r, y - r, x + r, y + r)
        Can.after(100, bouge)

def efface():
    Can.delete(ALL)

Vous allez maintenant devoir améliorer ce code pour répondre aux défis proposés dans l'exercice suivant:

Exo

  1. La balle revient au début de la ligne quand elle arrive à la fin de celle-ci(voir comment utiliser le modulo...).
  2. La balle rebondit.
  3. La balle part au milieu du canvas dans une direction choisit aléatoirement.
  4. La balle rebondit sur tous les côtés!

Pour finir en beauté!

Si vous avez bien accompli les missions précédentes, vous êtes aptes à réaliser ce dernier exercice. Bon courage!

Exo

Vous allez maintenant devoir:

  • créer deux balles de couleurs différentes et de même rayon
  • les animer dans le canvas sans qu'elles aient la même direction.
  • elles rebondissent sur les bords.
  • les balles s'arrêtent lorsqu'elles rentrent en collision
  • la première balle contamine la seconde en lui donnant sa couleur lors d'une collision.
  • créer cinq balles qui n'ont pas les mêmes propriétés.