INTRODUCTION |
Pour le moment, notre compréhension de l’ordinateur se limite à une machine qui effectue des instructions de manière séquentielle, les unes après les autres. L’exécution du programme peut être influencée par certaines conditions et parcourir des boucles. Les structures de programmation que l’on vient de décrire sont appelées séquence, sélection et itération. Böhm et Jacobini ont montré dans un articlefameux datant de 1966 que n’importe quelle procédure de calcul (algorithme) pouvait être réalisée uniquement à l’aide de ces trois structures de programmation fondamentales. Ce modèle d’exécution du programme n’est cependant vrai que dans la mesure où le programme n’est soumis à aucune influence extérieure. Par exemple, on peut imaginer interrompre à tout moment un programme en cliquant sur le bouton « fermer » avec la souris. Ce genre de processus nécessite par contre un nouveau concept de programmation : la gestion des événements (event handling en anglais). Nous avons déjà vu la base de la gestion d’événements dans le chapitre « Graphiques Tortue / Gestion d’événements ». Cela consiste en procédures du style « À chaque fois que l’événement e survient, la fonction f est exécutée ». L’implémentation en est simple et bien maitrisée depuis les premières heures de l’informatique, dans les années 50 du siècle passé. Il suffit de définir une fonction f (appelée routine d’interruption ou simplement interruption) qui n’est jamais appelée explicitement par le programme. Elle est pour ainsi dire inactive jusqu’à ce qu’un certain événement E comme une influence extérieure survienne et qu’elle soit appelée automatiquement par le système. De nos jours, on appelle ce genre de fonction des fonctions de rappel (callback en anglais) et on dit que la fonction de rappel est « déclenchée » par l’événement E. Souvent, les fonctions de rappel sont appelées avec des paramètres comportant des informations importantes concernant l’événement responsable du déclenchement. Ces informations permettent de savoir par exemple quel bouton de la souris a été cliqué et la position de la souris au moment du clic.
|
RÉAGIR AUX ÉVÉNEMENTS DE LA SOURIS |
On peut utiliser les événements de la souris dans le GPanel, de manière similaire aux graphiques tortue. Dans l’exemple ci-dessous, un disque vert est dessiné à la position actuelle de la souris lorsque le bouton gauche ou droit est actionné.
Ceci constitue la phase d'enregistrement de la fonction de rappel (callback registering en anglais). Pour enregistrer la fonction de rappel, il faut utiliser un paramètre nommé de la fonction makeGPanel() nommé mousePressed. from gpanel import * def onMousePressed(x, y): move(x, y) fillCircle(0.02) makeGPanel(mousePressed = onMousePressed) setColor("green") |
MEMENTO |
Une fonction de rappel n’est pas appelée par notre programme, mais de manière automatique lorsque survient l’événement. L’enregistrement de la fonction de rappel est réalisé par un paramètre nommé. On peut détecter les clics de la souris avec deux fonctions de rappel différentes : l’événement click et l’événement press. L’événement click n’est déclenché que lorsque le bouton de la souris est relâché mais l’événement press est déclenché immédiatement suite à la pression sur le bouton de la souris. |
DÉTECTER LE MOUVEMENT DE LA SOURIS |
from gpanel import * def onMouseMoved(x, y): move(x, y) setColor("red") fillCircle(.04) setColor("black") circle(.04) makeGPanel(mouseMoved = onMouseMoved) |
MEMENTO |
La fonction de rappel onMouseMoved(x, y) est enregistrée grâce au paramètre nommé mouseMoved. |
DESSIN LIBRE AVEC UN BOUTON DE LA SOURIS ENFONCÉ |
from gpanel import * def onMousePressed(x, y): move(x, y) def onMouseDragged(x, y): draw(x, y) makeGPanel(mousePressed = onMousePressed, mouseDragged = onMouseDragged) |
MEMENTO |
On peut enregistrer simultanémentplusieurs gestionnaires d’événements (fonctions de rappel, callbacks) en utilisant les paramètres nommés de la fonction makeGPanel(), séparés par une virgule. L’ordre dans lequel les paramètres nommés sont spécifiés importe peu. |
BOUTON GAUCHE ET BOUTON DROIT |
from gpanel import * def onMousePressed(x, y): if isLeftMouseButton(): pixColor = getPixelColor(x, y) if pixColor == makeColor("white"): return clear() setColor(pixColor) move(5, 5) fillCircle(2) if isRightMouseButton(): for i in range(5): move(9, 2 * i + 1) if i == 0: setColor("deep pink") if i == 1: setColor("green") if i == 2: setColor("yellow") if i == 3: setColor("deep sky blue") if i == 4: setColor("dark violet") fillRectangle(2, 2) makeGPanel(0, 10, 0, 10, mousePressed = onMousePressed) move(5, 5) fillCircle(2) |
MEMENTO |
Les gestionnaires d’événements (fonctions de rappel) sont déclenchés aussi bien par le bouton gauche que par le bouton droit. On peut déterminer lequel des deux boutons est responsable du déclenchement de l’événement grâce aux fonctions isLeftMouseButton() ou isRightMouseButton(). |
LIGNES ÉLASTIQUES |
Il se pose toutefois un petit problème : pour déplacer l’élastique provisoire lors de la construction de la ligne, il faut le supprimer et le redessiner à son nouvel emplacement à chaque petit mouvement de la souris, sans pour autant changer le dessin actuel. Si on effaçait la ligne provisoire en la peignant avec la couleur de fond, des petits trous de de cette même couleur feraient leur apparition aux points d’intersection avec les autres lignes déjà dessinées. Pour résoudre ce problème, il faut sauvegarder le dessin actuel au sein du gestionnaire onMousePressed. La suppression de l’élastique temporaire se réalise alors naturellement en restaurant cette sauvegarde temporaire. On peut sauver un gaphique à l’aide de storeGraphics() et le restaurer avec recallGraphics(). from gpanel import * def onMousePressed(x, y): global x1, y1, x2, y2 storeGraphics() x1 = x y1 = y x2 = x1 y2 = y1 setColor("red") def onMouseDragged(x, y): global x2, y2 recallGraphics() x2 = x y2 = y line(x1, y1, x2, y2) def onMouseReleased(x, y): setColor("white") if not (x1 == x2 and y1 == y2): line(x1, y1, x2, y2) else: recalGraphics() x1 = 0 y1 = 0 x2 = 0 y2 = 0 makeGPanel(mousePressed = onMousePressed, mouseDragged = onMouseDragged, mouseReleased = onMouseReleased) title("Press And Drag To Draw Lines") bgColor("blue") setColor("white") lineWidth(2) |
MEMENTO |
Voici un rappel des principes permettant de dessiner les lignes élastiques temporaires L’événement du clic géré par onMousePressed détermine le point initial de la ligne et s’occupe de sauvegarder le graphique actuel.Dans le gestionnaire onMouseDragged on restaure le graphique original précédemment sauvegardé. Dans un deuxième temps, on dessine la nouvelle ligne provisoire dont l’extrémité correspond aux coordonnées indiquées par les paramètres x, y et on sauvegarde ces nouvelles coordonnées de l’extrémité dans les variables globales x2 et y2. Dans le gestionnaire onMouseReleased qui survient lorsqu’on relâche le bouton, la ligne est définitivement dessinée mais uniquement si les coordonnées de l’extrémité de la ligne sont différentes de son origine. |
EXERCICES |
|
MATÉRIEL SUPPLÉMENTAIRE |
ENREGISTRER DES FONCTIONS DE RAPPEL AVEC DÉCORATEURS |
Au lieu d’utiliser des paramètres nommés de la fonction makeGPanel() pour enregistrer les fonctions de rappel servant de gestionnaires d’événements, il est possible d’utiliser à cette fin des décorateurs. Il est possible de décorer n’importe quelle fonction prenant deux paramètres x et y avec une ligne débutant par le symbole @ de sorte que la fonction décorée soit automatiquement enregistrée par TigerJython comme gestionnaire de l’événement (fonction de rappel) avec lequel elle est décorée et qu’elle soit donc automatiquement appelée par TigerJython dès que l’événement en question survient.
Voici comment on peut utiliser cette technique de décorateurs pour récrire de manière plus élégante le programme présenté plus haut permettant de dessiner un cercle en gardant le bouton de la souris enfoncé : from gpanel import * @onMousePressed def doIt(x, y): move(x, y) fillCircle(0.02) makeGPanel() setColor("green") |