La récursivité ou le Python qui se mord la queue !

Transcription

La récursivité ou le Python qui se mord la queue !
Spécilité ISN
La récursivité
ou
le Python qui se mord la queue !
Le but de la récursivité est de faire répéter automatiquement une action dans le module tkinter
sans avoir à générer de nouvel événement. Par exemple faire bouger un carré sur l'écran sans avoir
à appuyer ou cliquer sans cesse.
On va comprendre ce concept à l'aide d'un exemple très simple. On souhaite générer un
compteur qui s'incrémente automatiquement toutes les 500 ms.
A- Incrémenter le compteur à chaque click sur un bouton "+1"
Tout d'abord on crée une fenêtre contenant un afficheur et un bouton :
• Un clic sur le bouton doit appeler la fonction d'incrémentation du compteur
#import des modules
from tkinter import *
#définition des fonctions
def incr():
"""incrémentation du compteur cpt"""
global cpt
#la variable globale cpt est traitée par cette fonction donc on la déclare
cpt+=1
#incrémentation de cpt
aff.configure(text=cpt)
#modification du texte affiché dans la zone d'affichage "aff"
#variables globales
cpt=0
#Programme Ppal
fen=Tk() #fenêtre principale
aff=Label(fen,text="0",bg="#CCCCCC")
#zone d'affichage avec une couleur de fond exprimée en hexadécimal
aff.pack()
bt_1=Button(fen,text="+1",bg="yellow",command=incr())
#bouton d'incrémentation
bt_1.pack()
fen.mainloop() #boucle de détection d'événement
1ère étape satisfaisante : chaque clic sur le bouton permet l'incrémentation du compteur
B- Exécution automatique
C'est maintenant que le Python va se mordre la queue...
objectif : Un clic sur le bouton "+1" doit déclencher le lancement du compteur qui s'incrémentera de 1
après chaque demie seconde.
Pour cela on va utiliser la méthode "after" de la classe Tk().
syntaxe :
win.after(tps,fctn) appelle la fonction "fctn" du widget "win" après un temps "tps"
B M-L
1/3
Spécilité ISN
application à notre exemple :
on veut que le compteur s'incrémente tout seul après 500 ms. Il suffit donc de rappeler notre fonction
incr() dès qu'elle a terminé :
def incr():
"""incrémentation du compteur cpt"""
global cpt
cpt+=1
aff.configure(text=cpt)
fen.after(500,incr)
Youpi ! Ca marche !
Euh... tellement bien qu'on ne peut plus l'arrêter !!!!!
Pire encore, si on clique à nouveau sur le bouton "+1" le compteur s'emballe :-(
C- L'importance du flag.
De l'anglais "flag" ("drapeau"), cette variable va jouer le rôle d'un chef de gare (du temps d'avant)
levant ou abaissant un drapeau pour signifier le départ d'un train (pour les amateurs de foot on peut
faire la même analogie avec le juge de touche signalant un hors jeu ou tout autre événement).
Notre programme doit être commandé par 2 boutons :
• un pour autoriser l'incrémentation
• un pour arrêter l'incrémentation
#import des modules
from tkinter import *
#définition des fonctions
def incr():
"""incrémentation du compteur cpt"""
global cpt, flag
#le drapeau est maintenant pris en compte par la fonction
cpt+=1
aff.configure(text=cpt)
if flag == 1 :
# si elle est autorisée (flag=1) alors :
fen.after(500,incr)
# appel de l'incrémentation après 500 ms
def go():
"""autorisation d'incrémenter"""
global flag
#variable globale car elle sera utilisée par d'autres fonctions
flag=1
#drapeau à"1", on autorise l'incrémentation
incr()
#lancement de l'incrémentation
def stop():
"""arrêt de l'incrémentation"""
global flag
#Cf go()
flag=0
#drapeau à "0", on interdit l'incrémentation
#variables globales
cpt=0
flag=0
#Le compteur
#Le drapeau de contrôle
#Programme Ppal
fen=Tk()
B M-L
#fenêtre principale
2/3
Spécilité ISN
aff=Label(fen,text="0",bg="#CCCCCC") #zone d'affichage
aff.pack(side="top")
bt_1=Button(fen,text="GO !",bg="yellow",command=go) #bouton d'incrémentation
bt_1.pack(side="left")
bt_2=Button(fen,text="STOP",bg="red",command=stop) #bouton d'arrêt de l'incrémentation
bt_2.pack(side="right")
fen.mainloop() #boucle de détection d'événement
Et voilà le travail !
Il reste un dernier soucis : Si on clique à nouveau sur "GO !", le compteur s'emballe.
Cela vient du fait que l'on autorise une incrémentation alors qu'une précédente est déjà en cours (un
peu comme si on lançait un slalomeur alors que le précédent est encore en piste !
Il faut donc vérifier qu'aucune incrémentation n'est en cours (flag=0) avant d'en autoriser une
nouvelle. Pour cela on modifie la fonction go() de cette façon :
def go():
"""autorisation d'incrémenter"""
global flag
if flag == 0 :
#si pas d'incrémentation en cours :
flag=1
#drapeau à"1", on autorise l'incrémentation
incr()
B M-L
3/3