fonctions Evénement

Identification

Infoforall

8 - Fonction et événement


Vous savez maintenant réaliser des dessins avec Turtle.

Mais tout cela est bien statique. Comment permettre à l'utilisateur d'interagir avec notre programme ? A travers l'application graphique.

Nous allons voir aujourd'hui qu'on classe les fonctions en deux catégories lorsqu'on crée un programme :

  1. Les fonctions liées à l'interface
  2. Les fonctions qui ne gèrent que les données

Logiciel nécessaire pour l'activité : Python 3

Evaluation ✎ : question 02-06

1 - Interface graphique / texte

Interface graphique ou texte

Lorsqu'on crée un programme, on aime bien qu'il puisse interagir avec l'utilisateur.

Deux mondes coexistent donc :

  • D'un côté, le monde physique dans lequel l'utilisateur peut agir sur l'ordinateur et voir l'écran par exemple.
  • De l'autre côté, le programme qui tourne à l'intérieur de l'ordinateur

Pourtant, une interaction entre les deux est possible. Ce point d'interaction se nomme l'interface graphique (ou l'interface texte si on ne gère que le clavier et le clavier par exemple).

Les interactions se font dans les deux sens :

  • L'utilisateur peut transmettre des informations au programme en passant par l'interface
  • Le programme peut transmettre des informations à l'utilisateur en passant par l'interface.

L'interface graphique est donc composée d'un ensemble de programmes préexistant au votre. Cette interface est capable de faire le lien entre le monde extérieur et le code informatique interne.

Pour cela, elle a besoin de communiquer avec les composants matériels qui permettent à l'utilisateur de transmettre ou recevoir des informations de l'utilisateur.

Capteurs et actionneurs

Définitions

  • Un capteur est un composant capable de transmettre une information issue du monde physique à un système (électronique, informatique...)
  • Un actionneur est un composant capable de provoquer sur commande du système (informatique ou autre) une action dans le monde physique

01° Classer les trois composants visibles (écran, clavier, souris) en les mettant soit dans la catégorie des capteurs, soit dans la catégorie des actionneurs.

...CORRECTION...

La souris et le clavier sont des capteurs puisqu'on envoie des informations vers le système informatique.

L'écran est un actionneur : le système informatique parvient bien à modifier l'apparence de l'écran physique.

On notera qu'un écran TACTILE est à la fois un capteur et un actionneur.

Dans l'activité précédente, l'interface graphique était une interface Tkinter : si vous aviez laissé la souris stationnée tranquillement sur son icône, vous auriez pu voir apparaitre le mot Tk, pour Tkinter. C'est le nom de cette interface graphique.

La console de Python ou de Thonny est une autre interface graphique. Elle mime les interfaces purement textuelles, mais elle possède bien des menus accessibles en utilisant la souris par exemple.

2 - Evénement Click

Un peu d'interactivité maintenant.

Pour l'instant, nous sommes en programmation impérative : les instructions s'enchaînent les unes derrière les autres et on peut suivre le cheminement du programme.

Mais, on peut faire de la programmation événementielle : il s'agit de placer notre programme dans un certain état puis de surveiller les actions de l'utilisateur de façon à modifier l'état du programme en fonction des actions de l'utilisateur.

Exemple :

25 26 27 28 29 30 31 32
def dessin_au_click(x,y) : print(x) print(y) cercle("black", "grey", 5, x, y, 30) ecran = trt.Screen() ecran.setup(400,300) ecran.onclick(dessin_au_click)

Ligne 25 à 28 : déclaration et mise en mémoire d'une fonction. On zappe pour l'instant.

Ligne 30 : on crée et récupère la référence d'une application graphique ecran. Cela nous permettra d'agir sur la fenêtre graphique dans laquelle apparaissent les dessins. On remarquera que la référence est créé avec Screen (et pas avec Turtle qui permet de créer simplement un crayon-tortue dans cette fenêtre).

Ligne 31 : on agit sur la fenêtre graphique pour dimensionner l'écran.

Ligne 32 : on utilise la méthode onclick sur l'écran (voir FAQ éventuellement). On lui transmet la fonction qu'il faudra activer lorsqu'on détecte un clic de la souris sur notre écran : ici, cela va lancer automatiquement la fonction dessin_au_click.

✎ 02° Lancer et tester le programme suivant, avec quelques clics sur l'écran.

Le propre des fonctions événementielles est de recevoir des informations sur l'événement qui les ont déclenchées.

Vous noterez la présence de deux fonctions natives print permettant de faire du debug : on affiche les valeurs x et y reçus par la fonction.

Question : que semblent contenir automatiquement les paramètres x et y de la fonction événementielle dessin_au_click ?

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
import turtle as trt def stylo(couleur, largeur) : feutre = trt.Turtle() feutre.color(couleur) feutre.pensize(largeur) feutre.speed(7) return feutre def deplacer(feutre, x, y) : feutre.penup() # On lève la pointe feutre.setheading(0) # On oriente le crayon vers la droite (angle 0) feutre.setpos(x, y) feutre.pendown() # On abaisse la pointe def cercle(couleur, interne, largeur, x, y, rayon) : feutre = stylo(couleur, largeur) deplacer(feutre, x, y) feutre.fillcolor(interne) feutre.begin_fill() feutre.circle(rayon) feutre.end_fill() feutre.hideturtle() def dessin_au_click(x,y) : print(x) print(y) carre("black", "grey", 5, x, y, 30) ecran = trt.Screen() ecran.setup(400,300) ecran.onclick(dessin_au_click)

On remarquera qu'on dit automatiquement car si on regarde la ligne de l'appel de la fonction dessin_au_click, on voit bien qu'on ne transmet rien : on donne juste le nom de la fonction à activer, sans transmettre de valeurs ou de variables.

32
ecran.onclick(dessin_au_click)

Or, dans le prototype de la fonction, il y a bien des paramètres 

25
def dessin_au_click(x,y) :

En réalité, il y a une partie du code de résolution de l'action qui est ici caché : lorsqu'on clique avec la souris sur l'application graphique, cela déclenche un événement. Votre programme reçoit alors la position de la souris au moment du clic.

03° Que trouve-t-on donc nécessairement entre votre programme et la souris ?

...CORRECTION...

Il y a nécessairement un ensemble de programmes gérant l'interaction. Nous les nommerons INTERFACE GRAPHIQUE pour simplifier.

Du coup, nous allons divisé les fonctions utilisées dans nos programmes en deux catégories :

  • Les fonctions d'interface : ce sont les fonctions Python qui permettent de recevoir ou d'envoyer des informations vers une interface.
  • Les fonctions qui ne gèrent que les données et n'ont aucune interaction avec l'interface graphique.

04° Pourquoi peut-on dire que notre programme ne contient ici QUE des fonctions d'interface ?

Pourquoi peut-on dire que print est une fonction native d'interface ?

...CORRECTION...

On voit bien que toutes les fonctions agissent sur l'interface à travers le paramètre feutre qui est un objet Turtle.

De la même façon, print est une fonction d'interface avec la console. On dit qu'elle est native car ce n'est pas nous qui l'avons codé. Elle est présente de base dans Python, sans importation quelconque.

05° Identifier la ligne qui permet de gèrer la communication HUMAIN vers PROGRAMME.

...CORRECTION...

Il s'agit de la ligne 32 :

32
ecran.onclick(dessin_au_click)

3 - Hasard

Et si nous rajoutions un peu de hasard : lorsqu'on clique sur l'écran, nous aimerions bien par exemple que le cercle apparaisse un peu au hasard sur l'écran. Comment faire ?

Hasard avec le module random

En Python, la manière la plus simple est d'importer le module random.

On y trouve une fonction nommée randint dont le prototype est randint(min, max) qui renvoie un nombre entier aléatoire compris dans l'intervale [min;max].

Exemple :

>>> import random >>> z = random.randint(50,60) >>> z 53

Comme taper random est un peu long, on peut éventuellement donné un alias au module en utilisant le mot-clé as :

>>> import random as rd >>> z = rd.randint(-20,20) >>> z -12

✎ 06° Fournir le code (à taper dans la console interactive) permettant de générer un nombre aléatoire entre -10 et +10.

07° Modifier le programme (en rajoutant une importation) et la fonction dessin_click de façon à permettre maintenant au dessin d'apparaître n'importe où dans la fenêtre graphique. Voici à titre d'exemple, cette fonction où on impose de toujours dessiner au centre de l'écran. A vous de gérer le cas du hasard.

1 2 3 4
def dessin_au_click(x,y) : x = 0 y = 0 cercle("black", "grey", 5, x, y, 30)

Un exemple de solution similaire apparaît dans la partie suivante si vous bloquez (aux lignes 2-3 du premier code).

4 - Evénement Touche

Pour finir, regardons comment gérer le clavier avec une application graphique créée à l'aide de Turtle.

Imaginons qu'on désire tracer un cercle au hasard en appuyant sur la touche flèche du haut ('Up').

Voici un bout du programme et la fonction qui permettra de gérer l'événément :

1 2 3 4 5 6 7 8 9 10
def dessin_disque() : x = rd.randint(-200, 200) y = rd.randint(-150, 150) cercle("black", "red", 5, x, y, 30) ecran = trt.Screen() ecran.setup(400,300) ecran.onclick(dessin_au_click) ecran.onkey(dessin_disque, "Up") ecran.listen()

C'est sur la ligne 9 qu'on crée la liaison entre l'événement "Appui détecté sur la flèche HAUT" et la fonction à activer dans ce cas. On utilise pour cela la méthode onkey sur la fenêtre graphique.

Ligne 10 : une nouvelle méthode, listen, permet à l'application graphique ecran d'obtenir les notifications d'événements sur autre chose que la souris.

08° Sans activer le code : comment se nomme la fonction qui sera activée lorsqu'on clique sur la flèche HAUT ? Cette fonction reçoit-elle aussi des paramètres automatiques ?

...CORRECTION...

9
ecran.onkey(dessin_disque, "Up")

On voit sur la ligne précédente qu'on veut déclencher dessin_disque.

Si on regarde la déclaration de la fonction, notamment la première ligne, on peut voir qu'elle n'attend aucun paramètre : c'est bien qu'elle n'est pas censée en recevoir automatiquement cette fois.

1
def dessin_disque() :

09° Récupérer le code qui se trouve sous cette question. Modifier dessin_carre et compléter le programme principal : on veut qu'un appui sur 'C' ou 'c' provoque l'apparition d'un carré au hasard.

Une fois que cette partie fonctionne, faire de même avec les triangles et les touches 'T' et 't'.

NOte : on s'inspirera du code présent pour le disque : les tâches simples sont bien définies dans des fonctions et les tâches complexes font appel aux fonctions des tâches simples.

Exemple : dessin_disque fait appel à trace_disque qui, elle-même fait appel à :

  1. stylo pour créer une tortue feutre
  2. deplacer pour déplacer la tortue feutre
  3. cercle pour dessiner un cercle avec la tortue feutre
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
'''Documentation de ce fichier. Il comporte les fonctions suivantes : def nouveau_stylo(ecriture, fond, largeur) : def deplacer(feutre, x, y) : def trois(feutre, distance, angle) : def triangle(feutre, cote) : def trace_triangle(ecriture, fond, largeur, x, y, cote) : def quatre(feutre, distance, angle) : def carre(feutre, cote) : def trace_carre(ecriture, fond, largeur, x, y, cote) : def arc_de_cercle(feutre, rayon, angle) : def cercle(feutre, rayon) : def trace_disque(ecriture, fond, largeur, x, y, r) : def dessin_disque() def dessin_triangle() def dessin_carre() ''' # Partie Importation import turtle as trt import random as rd # Partie déclaration des fonctions d'interface def nouveau_stylo(ecriture, fond, largeur) : '''Renvoie la référence d'un stylo configuré''' feutre = trt.Turtle() feutre.color(ecriture) feutre.fillcolor(fond) feutre.pensize(largeur) feutre.speed(5) return feutre def deplacer(feutre, x, y) : '''Lève le feutre, déplace le feutre et abaisse le feutre''' feutre.penup() # On lève la pointe feutre.setpos(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe def trois(feutre, distance, angle) : '''Fait avancer le crayon de la distance, tourne de l'angle donné. Trois fois.''' feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) def triangle(feutre, cote) : '''Trace un triangle (equilatéral) à l'aide du crayon feutre''' trois(feutre, cote, 120) def trace_triangle(ecriture, fond, largeur, x, y, cote) : '''Trace un triangle (equilatéral) en (x, y) dans l'application graphique''' feutre = nouveau_stylo(ecriture, fond, largeur) deplacer(feutre, x, y) feutre.begin_fill() triangle(feutre, cote) feutre.end_fill() feutre.hideturtle() return feutre def quatre(feutre, distance, angle) : '''Fait avancer le crayon de la distance, tourne de l'angle donné. Quatre fois.''' feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) def carre(feutre, cote) : '''Trace un carré à l'aide du crayon feutre''' quatre(feutre, cote, 90) def trace_carre(ecriture, fond, largeur, x, y, cote) : '''Trace un carré en (x, y) dans l'application graphique''' feutre = nouveau_stylo(ecriture, fond, largeur) deplacer(feutre, x, y) feutre.begin_fill() carre(feutre, cote) feutre.end_fill() feutre.hideturtle() return feutre def arc_de_cercle(feutre, rayon, angle) : '''Trace l'arc de cercle voulu.''' feutre.circle(rayon, angle) def cercle(feutre, rayon) : '''Trace un cercle à l'aide du crayon feutre''' arc_de_cercle(feutre, rayon, 360) def trace_disque(ecriture, fond, largeur, x, y, rayon) : '''Trace un disque coloré en (x, y) dans l'application graphique''' feutre = nouveau_stylo(ecriture, fond, largeur) deplacer(feutre, x, y) feutre.begin_fill() cercle(feutre, rayon) feutre.end_fill() feutre.hideturtle() return feutre # Partie fonctions événementielles def dessin_disque() : x = rd.randint(-200, 200) y = rd.randint(-150, 150) trace_disque("black", "red", 5, x, y, 30) def dessin_triangle() : x = rd.randint(-200, 200) y = rd.randint(-150, 150) trace_triangle("black", "blue", 5, x, y, 50) def dessin_carre() : pass # Programme principal ecran = trt.Screen() ecran.setup(400,300) ecran.onkey(dessin_disque, "Up") ecran.listen()

...CORRECTION...

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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
'''Documentation de ce fichier. Il comporte les fonctions suivantes : def nouveau_stylo(ecriture, fond, largeur) : def deplacer(feutre, x, y) : def trois(feutre, distance, angle) : def triangle(feutre, cote) : def trace_triangle(ecriture, fond, largeur, x, y, cote) : def quatre(feutre, distance, angle) : def carre(feutre, cote) : def trace_carre(ecriture, fond, largeur, x, y, cote) : def arc_de_cercle(feutre, rayon, angle) : def cercle(feutre, rayon) : def trace_disque(ecriture, fond, largeur, x, y, r) : def dessin_disque() def dessin_triangle() def dessin_carre() ''' # Partie Importation import turtle as trt import random as rd # Partie déclaration des fonctions d'interface def nouveau_stylo(ecriture, fond, largeur) : '''Renvoie la référence d'un stylo configuré''' feutre = trt.Turtle() feutre.color(ecriture) feutre.fillcolor(fond) feutre.pensize(largeur) feutre.speed(5) return feutre def deplacer(feutre, x, y) : '''Lève le feutre, déplace le feutre et abaisse le feutre''' feutre.penup() # On lève la pointe feutre.setpos(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe def trois(feutre, distance, angle) : '''Fait avancer le crayon de la distance, tourne de l'angle donné. Trois fois.''' feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) def triangle(feutre, cote) : '''Trace un triangle (equilatéral) à l'aide du crayon feutre''' trois(feutre, cote, 120) def trace_triangle(ecriture, fond, largeur, x, y, cote) : '''Trace un triangle (equilatéral) en (x, y) dans l'application graphique''' feutre = nouveau_stylo(ecriture, fond, largeur) deplacer(feutre, x, y) feutre.begin_fill() triangle(feutre, cote) feutre.end_fill() feutre.hideturtle() return feutre def quatre(feutre, distance, angle) : '''Fait avancer le crayon de la distance, tourne de l'angle donné. Quatre fois.''' feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) feutre.forward(distance) feutre.left(angle) def carre(feutre, cote) : '''Trace un carré à l'aide du crayon feutre''' quatre(feutre, cote, 90) def trace_carre(ecriture, fond, largeur, x, y, cote) : '''Trace un carré en (x, y) dans l'application graphique''' feutre = nouveau_stylo(ecriture, fond, largeur) deplacer(feutre, x, y) feutre.begin_fill() carre(feutre, cote) feutre.end_fill() feutre.hideturtle() return feutre def arc_de_cercle(feutre, rayon, angle) : '''Trace l'arc de cercle voulu.''' feutre.circle(rayon, angle) def cercle(feutre, rayon) : '''Trace un cercle à l'aide du crayon feutre''' arc_de_cercle(feutre, rayon, 360) def trace_disque(ecriture, fond, largeur, x, y, rayon) : '''Trace un disque coloré en (x, y) dans l'application graphique''' feutre = nouveau_stylo(ecriture, fond, largeur) deplacer(feutre, x, y) feutre.begin_fill() cercle(feutre, rayon) feutre.end_fill() feutre.hideturtle() return feutre # Partie fonctions événementielles def dessin_disque() : x = rd.randint(-200, 200) y = rd.randint(-150, 150) trace_disque("black", "red", 5, x, y, 30) def dessin_triangle() : x = rd.randint(-200, 200) y = rd.randint(-150, 150) trace_triangle("black", "blue", 5, x, y, 50) def dessin_carre() : x = rd.randint(-200, 200) y = rd.randint(-150, 150) trace_carre("black", "yellow", 5, x, y, 50) # Programme principal ecran = trt.Screen() ecran.setup(400,300) ecran.onkey(dessin_disque, "Up") ecran.onkey(dessin_triangle, "T") ecran.onkey(dessin_triangle, "t") ecran.onkey(dessin_carre, "C") ecran.onkey(dessin_carre, "c") ecran.listen()

5 - FAQ

Et left, forward... Ce sont des fonctions aussi non ?

Comme vous avez dû le remarquer, on peut agir sur le crayon / feutre en utilsant des sortes de fonctions : left, forward...

La syntaxe est néanmoins différentes de celles des fonctions : on doit placer le nom de l'objet sur lequel on agit, placer un point puis placer le nom de la fonction.

1 2
feutre.forward(distance) feutre.left(angle)

Si vous regardez bien, vous pourrez d'ailleurs voir qu'elles sont en rouge foncé, contrairement aux fonctions qui sont en rouge clair.

Pourquoi ? Tout simplement car ces fonctions portent un nom particulier.

On les nomme des méthodes : ce sont des fonctions incorporées de base à l'intérieur de certaines structures qu'on nomme des objets. Comme notre Turtle.

Si vous regardez les codes Python que vous pouvez trouver sur le Web, vous risquez de tomber assez régulièrement sur ces drôles d'appels de fonctions avec le point.

En résumé, il s'agit juste de fonctions incorporées et on les nomme méthodes.

La codification est donc : objet.methode().

Activité publiée le 01 11 2020
Dernière modification : 01 11 2020
Auteur : ows. h.