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