deutsch     english    français     Imprimer

 

3.7 ÉVÉNEMENTS DE LA SOURIS

 

 

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.


CONCEPTS DE PROGRAMMATION: Programme dirigé par les événements, fonction de rappel (callback), enregistrement de fonctions de rappels (callback registering)

 

 

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é.

Dans un premier temps, on définit au sein d’une fonction dont le nom est choisi arbitrairement ce qui doit se passer lorsque survient un clic sur le bouton de la souris. Un choix raisonnable serait par exemple onMousePressed(),puisqu’il décrit à merveille ce que fait la fonction. Lorsqu’elle est appelée par le système en réponse à un événement, la fonction de rappel en question reçoit en tant que paramètre les coordonnées de la souris au moment du clic. Une fois cette fonction de rappel définie, il est nécessaire de renseigner le système qu’il doit appeler automatiquement cette fonction à chaque clic de la souris.

 

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")              
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

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

 

Les mouvements de la souris peuvent également être reconnus comme des événements se succédant rapidement lorsque la souris bouge. Le paramètre correspondant de makeGPanel() est mouseMoved.

Le programme ci-dessous dessine un disque rouge avec une bordure noire à chaque appel de la fonction de rappel. Il permet ainsi de dessiner des images en forme de tubes avec la souris.

 

from gpanel import *      

def onMouseMoved(x, y):
    move(x, y)
    setColor("red")
    fillCircle(.04) 
    setColor("black")
    circle(.04)  

makeGPanel(mouseMoved = onMouseMoved)
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

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É

 

Vous êtes maintenant capables d’écrire un programme de dessin simple permettant de dessiner librement une figure à l’aide de la souris. Il suffit pour cela de l’événement drag (glissé) qui est déclenché par successions rapides lorsque la souris est bougée avec le bouton enfoncé. La logique appliquée par le programme est simple : déplacer le curseur graphique à la position de la souris lorsque survient l'événement du clic (press) dessiner ensuite une ligne avec draw() au sein du gestionnaire de l’événement (callback) de l’événement drag.

 
from gpanel import *

def onMousePressed(x, y):
    move(x, y)

def onMouseDragged(x, y):
    draw(x, y)

makeGPanel(mousePressed = onMousePressed, 
           mouseDragged = onMouseDragged)
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

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

 

Comme vous l’avez sans doute remarqué, les événements de la souris sont déclenchés aussi bien par le bouton gauche que par le bouton droit. Si l’on veut distinguer les deux boutons, il faut utiliser la fonction isLeftMouseButton() et isRightMouseButton(). Celles-ci retournent True lorsque le bouton gauche, respectivement droit, est impliqué dans le déclenchement de l’événement.

Lorsque l’on clique sur le bouton droit de la souris, le programme ouvre une palette de couleurs permettant de choisir la couleur de remplissage du disque à l’aide du bouton gauche de la souris.
 
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)
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

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

 

Pour dessiner des lignes droites avec un programme de dessin, on marque en général le point initial par un clic de souris. En glissant la souris (drag), on crée ainsi une ligne temporaire semblable à un élastique qui est fixé au point de départ.  Lorsque la ligne provisoire est satisfaisante, on relâche le bouton de la souris et la ligne sera dessinée de manière définitive.

Pour réaliser cette fonctionnalité, il faut trois gestionnaires d’événements (fonctions de rappel): onMousePresssed, onMouseDragged et onMouseReleased.
 

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)
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

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

 

1.


Dessiner un disque vert qui doit passer au rouge lorsqu’il est survolé par le curseur de la souris. Il doit repasser au vert lorsque que le curseur de la souris ressort du disque.

Créer la fenêtre GPanel avec les paramètres
makeGPanel(-10, 10, -10, 10,
                   mouseMoved = onMouseMoved)

 



2.

Réaliser un programme qui dessine un segment droit à chaque clic de la souris et permettant ainsi de réaliser des images semblables à celle montrée ci-contre.
 


3.


Développer un programme qui dessine des figures tubulaires lorsque la souris est déplacée avec un bouton enfoncé. Pendant le mouvement, l’épaisseur des tubes doit croître de 0.01 à 0.1 par pas réguliers et retourner ensuite à son épaisseur d’origine.

 


4.

Développer un programme permettant de dessiner des rectangles verts sur fond noir. On souhaite pouvoir dessiner un rectangle élastique temporaire lors du glissé de la souris avant que ce rectangle ne soit définitivement fixé lorsque le bouton est relâché. Utiliser la fonction rectangle(x1, y1, x2, y2) où les coordonnées passées en paramètre correspondent aux sommets supérieur gauche et inférieur droit du rectangle.
 

 

 

   

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 les décorateurs disponibles :

@onMousePressed

Une touche de la souris a été pressée

@onMouseReleased

Une touche de la souris a été relâchée

@onMouseClicked

Une touche de la souris a été pressée et relâchée

@onMouseDragged

La souris est déplacée avec le bouton enfoncé

@onMouseMoved

La souris est déplacée sans qu’un bouton ne soit enfoncé

@onMouseEntered

La souris entre dans la fenêtre graphique

@onMouseExited

La souris sort de la fenêtre graphique

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")         
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)