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

24.3. Le widget zone de dessin (DrawingArea) et le dessin

Passons au processus de dessin sur l'écran. Le widget que l'on utilise pour ceci est le widget DrawingArea (voir le Chapitre 12, La zone de dessin (Drawing Area)). Un tel widget est essentiellement une fenêtre X et rien de plus. Il s'agit d'une toile vide sur laquelle nous pouvons dessiner ce que nous voulons. On créé ce widget par l'appel à :

  darea = gtk.DrawingArea()

On peut lui donner Une taille par défaut par l'appel :

  darea.set_size_request(largeur, hauteur)

Cette taille par défaut peu être surchargée, comme pour tous les widgets, en appelant la méthode set_size_request() et celle-ci, à son tour, peut être surchargée si l'utilisateur modifie manuellement la taille de la fenêtre contenant la zone de dessin.

Il faut noter que lorsque l'on crée un widget DrawingArea, on est complètement responsable du dessin du contenu. Si la fenêtre est cachée puis redécouverte, on reçoit un événement d'exposition et on doit redessiner ce qui avait été caché auparavant.

Devoir se rappeler tout ce qui a été dessiné à l'écran pour pouvoir correctement le redessiner peut s'avérer, et c'est un euphémisme, pénible. De plus, cela peut être visible si des portions de la fenêtre sont effacées puis redessinées étape par étape. La solution à ce problème est d'utiliser un pixmap d'arrière-plan hors écran. Au lieu de dessiner directement sur l'écran, on dessine sur une image stockée dans la mémoire du serveur et non affichée, puis, lorsque l'image change ou lorsque de nouvelles parties de l'image sont affichées, on copie les parties correspndantes sur l'écran.

Pour créer un pixmap hors écran, on appelle la fonction :

  pixmap = gtk.gdk.Pixmap(fenetre, largeur, hauteur, profondeur=-1)

Le paramètre fenetre désigne une fenêtre gtk.gdk.Window d'où ce pixmap tire certaines de ses propriétés. Les paramètres largeur et hauteur précisent la taille du pixmap, profondeur précise la profondeur de couleur (c'est-à-dire le nombre de bits par pixel) de la nouvelle fenêtre. Si cette profondeur vaut -1 ou n'est pas indiquée, elle correspondra à celle de fenetre.

On crée le pixmap dans notre gestionnaire "configure_event". Cet événement est généré à chaque fois que la fenêtre change de taille, y compris lors de sa création initiale.

    32   # Création d'un nouveau pixmap d'arrière-plan de la taille voulue
    33   def configure_event(widget, event):
    34       global pixmap
    35   
    36       x, y, largeur, hauteur = widget.get_allocation()
    37       pixmap = gtk.gdk.Pixmap(widget.window, largeur, hauteur)
    38       pixmap.draw_rectangle(widget.get_style().white_gc,
    39                             True, 0, 0, largeur, hauteur)
    40   
    41       return True
 

L'appel à draw_rectangle() initialise le pixmap à blanc. Nous en dirons plus tout à l'heure.

Le gestionnaire d'événement d'exposition copie alors simplement la partie utile du pixmap sur la zone de dessin (widget) en utilisant la méthode draw_pixmap(). On détermine la zone à redessiner en utilisant l'attribut event.area de l'événement d'exposition) :

    43   # Redessine l'écran à partir du pixmap d'arrière-plan
    44   def expose_event(widget, event):
    45       x , y, largeur, hauteur = event.area
    46       widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL],
    47                                   pixmap, x, y, x, y, largeur, hauteur)
    48       return False

On a vu comment garder l'écran à jour avec notre pixmap, mais comment dessine-t-on réellement ce que l'on veut dans le pixmap ? Il existe un grand nombre d'appels dans PyGTK pour dessiner sur des dessinables. Un dessinable est simplement quelque chose sur lequel on peut dessiner. Cela peut être une fenêtre, un pixmap, ou un bitmap (une image en noir et blanc). On a déjà vu plus haut deux de ces appels, draw_rectangle() et draw_pixmap(). En voici La liste complète :

  drawable.draw_point(gc, x, y)

  drawable.draw_line(gc, x1, y1, x2, y2)

  drawable.draw_rectangle(gc, fill, x, y, width, height)

  drawable.draw_arc(gc, fill, x, y, width, height, angle1, angle2)

  drawable.draw_polygon(gc, fill, points)

  drawable.draw_drawable(gc, src, xsrc, ysrc, xdest, ydest, width, height)

  drawable.draw_points(gc, points)

  drawable.draw_lines(gc, points)

  drawable.draw_segments(gc, segments)

  drawable.draw_rgb_image(gc, x, y, width, height, dither, buffer, rowstride)

  drawable.draw_rgb_32_image(gc, x, y, width, height, dither, buffer, rowstride)

  drawable.draw_gray_image(gc, x, y, width, height, dither, buffer, rowstride)

Les méthodes des zones de dessin sont identiques à celles des dessinables, ainsi on peut se reporter aux méthodes décrites dans la Section 12.2, « Les méthodes pour dessiner » pour plus de détails sur celles-ci. Toutes ces méthodes partagent les mêmes premiers arguments, le premier étant le contexte graphique (gc).

Un contexte graphique encapsule l'information sur des éléments comme la couleur de premier et d'arrière plan et la largeur de ligne. PyGTK possède un ensemble complet de fonctions pour créer et manipuler les contextes graphiques, mais, pour faire simple, nous n'utiliserons que les contextes graphiques prédéfinis. Reportez-vous à la Section 12.1, « Le contexte graphique » pour plus d'informations sur les contextes graphiques. Chaque widget posséde un style associé (qui peut être modifié dans un fichier gtkrc, voir la Chapitre 23, Les fichiers de style rc GTK. Celui-ci, entre autres choses, stocke plusieurs contextes graphiques. Quelques exemples d'accès à ces contextes graphiques :

  widget.get_style().white_gc

  widget.get_style().black_gc

  widget.get_style().fg_gc[STATE_NORMAL]

  widget.get_style().bg_gc[STATE_PRELIGHT]

Les champs fg_gc, bg_gc, dark_gc et light_gc sont indexés par un paramètre qui peut prendre les valeurs suivantes :

  STATE_NORMAL,
  STATE_ACTIVE,
  STATE_PRELIGHT,
  STATE_SELECTED,
  STATE_INSENSITIVE

Par exemple, pour STATE_SELECTED, la couleur de premier plan par défaut est blanc, la couleur d'arrière plan par défaut est bleu foncé.

La fonction draw_brush(), qui réalise le dessin sur le pixmap est alors :

    50   # Dessine un rectangle sur l'écran
    51   def brosse_dessin(widget, x, y):
    52       rect = (int(x-5), int(y-5), 10, 10)
    53       pixmap.draw_rectangle(widget.get_style().black_gc, True,
    54                             rect[0], rect[1], rect[2], rect[3])
    55       widget.queue_draw_area(rect[0], rect[1], rect[2], rect[3])

Après avoir dessiné le rectangle représentant la brosse sur le pixmap, on appelle la fonction :

  widget.queue_draw_area(x, y, width, height)

... qui indique à X que cette zone nécessite d'être mise à jour. X génèrera éventuellement un événement d'exposition (en combinant peut-être les zones passés dans plusieurs appels à draw()) ce qui forcera le gestionnaire d'événement d'exposition à recopier les parties adéquates à l'écran.

Nous avons maintenant couvert entièrement le programme de dessin, sauf quelques détails banals comme la création de la fenêtre principale.