Vous êtes à peu près ici : Accueil  »   tutoriel PyGTK  »   PyGTK : sommaire

24.2. Gestion des évènements

Les signaux GTK+ que nous avons déjà vus concernent les actions de haut niveau, comme la sélection d'un choix d'un menu. Cependant, il est utile quelquefois de connaître des possibilités de plus bas niveau, comme le déplacement de la souris, ou l'appui sur une touche. Il existe aussi des signaux GTK+ correspondants à ces événements de bas niveau. Les gestionnaires de ces signaux possèdent un paramètre supplémentaire : un objet gtk.gdk.Event contenant des informations sur l'événement. Par exemple, les gestionnaires des événements de déplacement recoivent un paramètre gtk.gdk.Event contenant une information GdkEventMotion qui contient (en partie) ces attributs :

  type    # type
  window  # fenêtre
  time    # temps
  x
  y
    ...
  state   # état
    ...

window est la fenêtre dans laquelle l'événement est survenu.

x et y fournissent les coordonnées de l'événement.

type sera initialisé avec le type de l'événement, ici MOTION_NOTIFY. Ces types (du module gtk.gdk) sont :

NOTHING                un code spécial pour indiquer un événement nul.

DELETE                 le gestionnaire de fenêtre a demandé que la fenêtre de plus haut niveau soit
                       cachée ou détruite, d'habitude quand l'utilisateur clique sur
                       une icône spéciale dans la barre de titre.

DESTROY                la fenêtre a été détruite.

EXPOSE                 tout ou partie de la fenêtre est devenu visible et doit être
                       redessiné.

MOTION_NOTIFY          le pointeur (d'habitude une souris) a bougé.

BUTTON_PRESS           on a effectué un clic sur un bouton de la souris.

_2BUTTON_PRESS         on a effectué un double-clic sur un bouton de la souris 
                       (deux clics en un bref temps) sur un bouton de la souris.
                       Note : chaque clic génère aussi un événement BUTTON_PRESS.

_3BUTTON_PRESS         on a effectué un triple-clic sur un bouton de la souris sur 
                       un temps bref. Note : chaque clic génère aussi un 
                       événement BUTTON_PRESS.

BUTTON_RELEASE         le bouton de la souris a été relaché.

KEY_PRESS              on a appuyé sur une touche.

KEY_RELEASE            la touche a été relachée.

ENTER_NOTIFY           le pointeur est entré dans la fenêtre.

LEAVE_NOTIFY           le pointeur est sorti de la fenêtre.

FOCUS_CHANGE           le focus de clavier est dans ou a quitté la fenêtre.

CONFIGURE              la taille, position ou ordre d'empilage a changé. Noter que GTK+ 
                       n'utilisa pas ces événements pour les fenêtres enfants GDK_WINDOW_CHILD.

MAP                    la fenêtre a été mappée.

UNMAP                  la fenêtre n'est plus mappée.

PROPERTY_NOTIFY        une propriété de la fenêtre a été modifiée ou supprimée.

SELECTION_CLEAR        l'application a perdu la propriété d'une sélection.

SELECTION_REQUEST      une autre application a réclamé la sélection.

SELECTION_NOTIFY       une sélection a été reçue.

PROXIMITY_IN           un dispositif d'entrée a bougé en contact avec une surface sensible
                       (par ex. un écran tactile ou tablette graphique).

PROXIMITY_OUT          un dispositif d'entrée a bougé en coupant le contact avec une surface sensible

DRAG_ENTER             la souris est entrée dans la fenêtre pendant une opération glisser.

DRAG_LEAVE             la souris est sortie de la fenêtre pendant une opération glisser.

DRAG_MOTION            la souris a bougé dans la fenêtre pendant une opération glisser.

DRAG_STATUS            l'état de l'opération glisser démarrée par la fenêtre a changé.

DROP_START             une opération déposer sur la fenêtre a démarrée.

DROP_FINISHED          une opération déposer initiée par la fenêtre est accomplie.

CLIENT_EVENT           un message d'une autre application a été reçu.

VISIBILITY_NOTIFY      l'état de visibilité de la fenêtre a changé.

NO_EXPOSE              indique que la région source est totalement disponible lorsque des extraits du 
                       dessinable sont copiés. Ceci ne présente pas d'intérêt.

SCROLL                 ?

WINDOW_STATE           ?

SETTING                ?

Le paramètre state indique l'état du modificateur lorsque l'événement s'est produit (c'est-à-dire quelles sont les touches de modification et les boutons de souris qui ont été pressés). Il s'agit d'une opération de bit OR de certaines des valeurs (du module gtk.gdk) suivantes :

  SHIFT_MASK    # masque de majuscules
  LOCK_MASK     # masque de majuscules bloquées
  CONTROL_MASK  # masque de contrôle
  MOD1_MASK     # masque du modificateur 1   
  MOD2_MASK     # masque du modificateur 2    
  MOD3_MASK     # masque du modificateur 3    
  MOD4_MASK     # masque du modificateur 4    
  MOD5_MASK     # masque du modificateur 5    
  BUTTON1_MASK  # masque du bouton 1  
  BUTTON2_MASK  # masque du bouton 2  
  BUTTON3_MASK  # masque du bouton 3  
  BUTTON4_MASK  # masque du bouton 4  
  BUTTON5_MASK  # masque du bouton 5  

Comme pour les autres signaux, on appelle la méthode connect() pour déterminer ce qui se passe lorsqu'un événement survient. Mais on doit aussi faire en sorte que GTK+ sache de quels événements nous voulons être avertis. Pour ce faire, on appelle la méthode :

  widget.set_events(events)

... où events définit les événements qui nous intéressent. Il s'agit d'une opération de bit OR de constantes qui indiquent différent types d'événements. Pour référence ultérieure, les types d'événements (du module gtk.gdk) sont :

  EXPOSURE_MASK
  POINTER_MOTION_MASK
  POINTER_MOTION_HINT_MASK
  BUTTON_MOTION_MASK     
  BUTTON1_MOTION_MASK    
  BUTTON2_MOTION_MASK    
  BUTTON3_MOTION_MASK    
  BUTTON_PRESS_MASK      
  BUTTON_RELEASE_MASK    
  KEY_PRESS_MASK         
  KEY_RELEASE_MASK       
  ENTER_NOTIFY_MASK      
  LEAVE_NOTIFY_MASK      
  FOCUS_CHANGE_MASK      
  STRUCTURE_MASK         
  PROPERTY_CHANGE_MASK
  VISIBILITY_NOTIFY_MASK
  PROXIMITY_IN_MASK      
  PROXIMITY_OUT_MASK
  SUBSTRUCTURE_MASK

Il y a quelques points subtils qui doivent être observés lorsqu'on appelle la méthode set_events(). D'abord, elle doit être appelée avant que la fenêtre X d'un widget GTK soit créée. En pratique, cela signifie que l'on doit l'appeler immédiatement après avoir créé le widget. Ensuite, le widget doit faire partie de ceux réalisés avec une fenêtre X associée. Pour des raisons d'efficacité, de nombreux types de widgets n'ont pas de fenêtre propre, mais se dessinent dans la fenêtre de leur parent. Ces widgets sont :

  gtk.Alignment
  gtk.Arrow
  gtk.Bin
  gtk.Box
  gtk.Image
  gtk.Item
  gtk.Label
  gtk.Layout
  gtk.Pixmap
  gtk.ScrolledWindow
  gtk.Separator
  gtk.Table
  gtk.AspectFrame
  gtk.Frame
  gtk.VBox
  gtk.HBox
  gtk.VSeparator
  gtk.HSeparator

Pour capturer les événements pour ces widgets, on doit utiliser un widget EventBox. Voir la Section 10.1, « La boîte à évènement (EventBox) » pour plus de détails.

Voici les attributs d'événement qui sont définis par PyGTK pour chaque type d'événement :

événement               type              # type
                        window            # fenêtre
                        send_event        # événement transmis

NOTHING
DELETE
DESTROY                                   # pas d'attribut supplémentaire

EXPOSE                 area               # zone
                       count              # nombre

MOTION_NOTIFY          time               # temps
                       x
                       y
                       pressure           # pression
                       xtilt              # inclinaison en x
                       ytilt              # inclinaison en y
                       state              # état
                       is_hint            # est indice
                       source             # source
                       deviceid           # identifiant de dispositif
                       x_root             # racine x
                       y_root             # racine y

BUTTON_PRESS
_2BUTTON_PRESS
_3BUTTON_PRESS
BUTTON_RELEASE         time               # temps
                       x
                       y
                       pressure           # pression
                       xtilt              # inclinaison en x
                       ytilt              # inclinaison en y
                       state              # état
                       button             # bouton
                       source             # source
                       deviceid           # identifiant de dispositif
                       x_root             # racine x
                       y_root             # racine y

KEY_PRESS
KEY_RELEASE            time               # temps
                       state              # état
                       keyval             # valeur de clé
                       string             # chaîne de caractères

ENTER_NOTIFY
LEAVE_NOTIFY           subwindow          # sous-fenêtre
                       time               # temps
                       x
                       y
                       x_root             # racine x
                       y_root             # racine y
                       mode               # mode
                       detail             # détail
                       focus              # focus
                       state              # état

FOCUS_CHANGE           _in                # dans

CONFIGURE              x
                       y
                       width              # largeur
                       height             # hauteur

MAP
UNMAP                                     # pas d'attribut supplémentaire

PROPERTY_NOTIFY        atom               # atome
                       time               # temps
                       state              # état

SELECTION_CLEAR
SELECTION_REQUEST
SELECTION_NOTIFY       selection          # sélection
                       target             # cible
                       property           # propriété
                       requestor          # demandeur
                       time               # temps

PROXIMITY_IN
PROXIMITY_OUT          time               # temps
                       source             # source
                       deviceid           # identifiant de dispositif

DRAG_ENTER
DRAG_LEAVE
DRAG_MOTION
DRAG_STATUS
DROP_START
DROP_FINISHED          context            # contexte
                       time               # temps
                       x_root             # racine x
                       y_root             # racine x

CLIENT_EVENT           message_type       # type de message
                       data_format        # format de données
                       data               # données

VISIBILTY_NOTIFY       state              # état

NO_EXPOSE                                 # pas d'attribut supplémentaire

24.2.1. Gestion des évènements dans Scribble

Pour notre programme de dessin, on veut savoir quand le bouton de la souris est pressé et quand la souris est déplacée, nous indiquons donc POINTER_MOTION_MASK et BUTTON_PRESS_MASK. On veut aussi savoir quand il est nécessaire de redessiner notre fenêtre, on indique donc EXPOSURE_MASK. Bien que nous voulions être avertis via un événement Configure, d'un redimensionnement de la fenêtre, on n'a pas besoin de préciser le drapeau STRUCTURE_MASK correspondant car il est automatiquement signalé pour chaque fenêtre.

Il peut cependant y avoir un problème en indiquant seulement POINTER_MOTION_MASK. Cela fera que le serveur ajoutera un nouvel événement de déplacement à la file des événements à chaque fois que l'utilisateur déplace la souris. Imaginons que cela prenne 0,1 seconde pour gérer un événement de déplacement, mais si le serveur X ajoute un nouvel événement de déplacement dans la queue toutes les 0,05 secondes, nous serons vite à la traîne de l'utilisateur. Si l'utilisateur dessine pendant 5 secondes, cela nous prendra 5 secondes de plus pour le traiter après qu'il ait relâché le bouton de la souris ! Ce que l'on voudrait, c'est ne récupérer qu'un événement de déplacement pour chaque événement que l'on traite. Pour cela, il faut préciser POINTER_MOTION_HINT_MASK.

Quand nous indiquons POINTER_MOTION_HINT_MASK, le serveur nous envoit un événement de déplacement la première fois que le pointeur se déplace après son entrée dans la fenêtre, ou après un événement d'appui ou de relâchement d'un bouton. Les événements de déplacement suivants seront supprimés jusqu'à ce que l'on demande explicitement la position du pointeur en utilisant la méthode gtk.gdk.Window :

  x, y, mask = window.get_pointer()

... où window est un objet gtk.gdk.Window, les paramètres x et y sont les coordonnées du pointeur et mask est le masque modificateur pour détecter les touches pressées. (Il existe une méthode get_pointer() pour gtk.Widget qui fournit la même information que la méthode gtk.gdk.Window get_pointer() mais ne retourne pas l'indication de masque des touches

Le programme exemple scribblesimple.py montre l'utilisation de base des événements et des gestionnaires d'événements. La Figure 24.2, « Exemple de Scribble simple » illustre le programme :

Figure 24.2. Exemple de Scribble simple

Exemple de Scribble simple

Les gestionnaires d'événements sont connectés à la zone de dessin grâce aux ligne suivantes :

    92       # Signaux utilisés pour gérer le pixmap hors écran
    93       zone_dessin.connect("expose_event", expose_event)
    94       zone_dessin.connect("configure_event", configure_event)
    95   
    96       # Signaux d'événements
    97       zone_dessin.connect("motion_notify_event", motion_notify_event)
    98       zone_dessin.connect("button_press_event", bouton_press_event)
    99   
   100       zone_dessin.set_events(gtk.gdk.EXPOSURE_MASK
   101                               | gtk.gdk.LEAVE_NOTIFY_MASK
   102                               | gtk.gdk.BUTTON_PRESS_MASK
   103                               | gtk.gdk.POINTER_MOTION_MASK
   104                               | gtk.gdk.POINTER_MOTION_HINT_MASK)
 

Les gestionnaires d'événement button_press_event() et motion_notify_event()dans scribblesimple.py sont ainsi :

    57   def bouton_press_event(widget, event):
    58       if event.button == 1 and pixmap != None:
    59           brosse_dessin(widget, event.x, event.y)
    60       return True
    61   
    62   def motion_notify_event(widget, event):
    63       if event.is_hint:
    64           x, y, etat = event.window.get_pointer()
    65       else:
    66           x = event.x
    67           y = event.y
    68           etat = event.state
    69       
    70       if etat & gtk.gdk.BUTTON1_MASK and pixmap != None:
    71           brosse_dessin(widget, x, y)
    72     
    73       return True

Les gestionnaires expose_event() et configure_event() seront décrits plus tard.