IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Apprendre à utiliser le module Python PyGTK 2.0


précédentsommairesuivant

X. Chapitre 10. Les widgets conteneurs

X-A. La boite à événement (EventBox)

Certains widgets GTK ne possèdent pas de fenêtre X associée, ils se dessinent juste sur leur widget parent. Donc, ils ne peuvent recevoir d'événements et si leurs dimensions sont incorrectes, ils ne s'ajustent pas et vous pouvez obtenir des chevauchements d'écriture, etc. Si vous voulez obtenir plus de ces widgets, il faut utiliser la boite à événement EventBox.

À première vue, la boite à événement EventBox peut sembler totalement inutile. Elle ne dessine rien sur l'écran et ne répond à aucun événement. Cependant, elle réalise une fonction : elle fournit une fenêtre X pour son widget enfant. Et comme beaucoup de widgets ne possèdent pas de fenêtre X associée, cette fonction est importante. Ne pas avoir de fenêtre X économise de la mémoire et augmente les performances, mais a aussi quelques inconvénients. Un widget sans fenêtre X ne peut recevoir un événement, ne peut réaliser aucun ajustement sur son contenu et ne peut posséder de couleur d'arrière-plan. Quoique le nom EventBox met en évidence la fonction de gestion d'événement, ce widget peut aussi être redimensionné, (et plus encore, voir l'exemple ci-dessous).

Pour créer une nouvelle boite à événement EventBox, faire :

 
Sélectionnez
  boite_evenement = gtk.EventBox()

Un widget enfant peut être ajouté à la boite à événement par :

 
Sélectionnez
  event_box.add(widget)

Le programme d'exemple eventbox.py illustre les deux utilisations de la boite à événement. Créer un label, inscrit dans une petite boite, possédant un arrière-plan vert et permettant, lors d'un clic sur ce label, de sortir du programme. Redimensionner la fenêtre permet les variations de taille du label. La Figure 10.1, « Exemple de boite à événement » montre l'affichage du programme.

Image non disponible
Figure 10.1. Exemple de boite à événement

Voici le code source du programme eventbox.py :

 
Sélectionnez
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.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# exemple eventbox.py

import pygtk
pygtk.require('2.0')
import gtk

class ExempleBoiteEvenement:
    def __init__(self):
        fenêtre = gtk.Window(gtk.WINDOW_TOPLEVEL)
        fenetre.set_title("Boite à événement")
        fenetre.connect("destroy", lambda w: gtk.main_quit())
        fenetre.set_border_width(10)

        # On crée une boite à événement et on l'ajoute à la fenêtre principale
        boite_evenement = gtk.EventBox()
        fenetre.add(boite_evenement)
        boite_evenement.show()

        # On crée un label long
        label = gtk.Label("Cliquer ici pour quitter, finir, terminer, sortir...")
        boite_evenement.add(label)
        label.show()

        # On fixe sa taille de départ (largeur, hauteur).
        label.set_size_request(110, 30)

        # On relie une action à la boite
        boite_evenement.set_events(gtk.gdk.BUTTON_PRESS_MASK)
        boite_evenement.connect("button_press_event", lambda w,e: gtk.main_quit())

        # D'autres choses pour lesquelles il faut une fenêtre X...
        boite_evenement.realize()
        boite_evenement.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND1))

        # Un arrière-plan en vert
        boite_evenement.modify_bg(gtk.STATE_NORMAL,
                            boite_evenement.get_colormap().alloc_color("green"))

        fenetre.show()

def main():
    gtk.main()
    return 0

if __name__ == "__main__":
    ExempleBoiteEvenement()
    main()

X-B. Le widget d'alignement (Alignment)

Le widget d'alignement Alignment permet de placer un widget dans sa fenêtre à une position et taille relatives à la taille du widget d'alignement lui-même. Ce qui peut être très utile pour, par exemple, centrer un widget dans sa fenêtre.

Il n'y a que deux appels(1) liés au widget d'alignement :

 
Sélectionnez
1.
2.
3.
  alignement = gtk.Alignment(xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0)

  alignement.set(xalign, yalign, xscale, yscale)

La fonction gtk.Alignment() crée un nouveau widget d'alignement Alignment avec les paramètres indiqués. La méthode set() permet de modifier les paramètres d'un widget d'alignement préexistant.

Les quatre paramètres d'alignement sont des nombres décimaux, compris entre 0.0 et 1.0. Les paramètres xalign et yalign influent sur la position du widget placé dans le widget d'alignement (voir gtk.Alignment dans le manuel de référence). Les propriétés d'alignement indiquent le rapport d'espace libre existant au-dessus et à gauche du widget enfant. Leurs valeurs sont comprises entre 0.0 (pas d'espace libre au-dessus ni à gauche) et 1.0 (tout l'espace libre au-dessus et à gauche). Bien sûr, si les échelles sont toutes les deux fixées à 1.0, les propriétés d'alignement n'auront pas d'effet, car le widget enfant s'étendra pour remplir tout l'espace disponible.

Les paramètres xscale et yscale indiquent la fraction d'espace libre qui sera absorbé par le widget enfant. Leurs valeurs sont comprises entre 0.0 (le widget n'absorbe aucun espace) et 1.0 (le widget absorbe tout l'espace libre.

Un exemple(2) pour rendre les choses plus claires :

  • un widget bouton (gtk.Button) de 32x32 pixels est placé dans un widget d'alignement de 256 pixels de large sur 128 pixels de haut. Les valeurs d'alignement sont, xalign=0.25, yalign=0.25, xscale=0.25, yscale=0.25 ;
  • l'espace libre horizontal vaut 256-32=224 pixels, l'espace libre vertical vaut 128-32=96 pixels.

Le bouton absorbe 0.25x224=56 pixels de l'espace horizontal et 0.25x96=24 pixels de l'espace vertical. Sa taille devient donc 32+56=88 pixels de large pour 32+24=56 pixels de haut. Ce qui nous laisse 256-88=168 pixels d'espace libre horizontal et 128-56=72 pixels d'espace libre vertical. Puisque les valeurs de xalign et yalign sont de 0.25, l'espace libre horizontal est distribué entre 0.25x168=42 pixels à gauche du bouton et le reste à droite ; l'espace libre vertical est réparti entre 0.25x72=18 pixels au-dessus et le reste en dessous.

On peut ajouter un widget enfant à un widget d'alignement par :

 
Sélectionnez
  alignement.add(widget)

Pour un exemple d'utilisation du widget d'alignement, voir le programme progressbar.py sur la barre de progression.

X-C. Le conteneur Place (Fixed)

Le conteneur Place (Fixed) permet de disposer des widgets dans sa fenêtre, à une position précise, par rapport au coin supérieur gauche. Cette position peut être modifiée de manière dynamique.

Il existe seulement trois appels associés au conteneur Fixed

 
Sélectionnez
1.
2.
3.
4.
5.
  conteneur_place = gtk.Fixed()

  conteneur_place.put(widget, x, y)

  conteneur_place.move(widget, x, y)

La fonction gtk.Fixed() crée un nouveau conteneur Fixed.

La méthode put() insère le widget dans le conteneur Fixed à la position indiquée par les paramètres x et y.

La méthode move() permet au widget indiqué d'être déplacé dans une nouvelle position.

Le programme fixed.py illustre l'utilisation du conteneur Fixed. La Figure 10.2, « Exemple de conteneur Fixed » montre le résultat.

Image non disponible
Figure 10.2. Exemple de conteneur Fixed

Voici le code du programme fixed.py :

 
Sélectionnez
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.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# exemple fixed.py

import pygtk
pygtk.require('2.0')
import gtk

class ExemplePlace:
    # Cette méthode de rappel déplace le bouton dans
    # le conteneur placé vers une nouvelle position
    def deplace_bouton(self, widget):
        self.x = (self.x+30)%300
        self.y = (self.y+50)%300
        self.place.move(widget, self.x, self.y) 

    def __init__(self):
        self.x = 50
        self.y = 50

        # On crée une nouvelle fenêtre
        fenetre = gtk.Window(gtk.WINDOW_TOPLEVEL)
        fenetre.set_title("Conteneur Fixed")

        # On connecte l'événement "destroy" au gestionnaire de signal 
        fenetre.connect("destroy", lambda w: gtk.main_quit())
 
        # On indique la largeur des bordures de la fenêtre
        fenetre.set_border_width(10)

        # On crée le conteneur Fixed (place)
        self.place = gtk.Fixed()
        fenetre.add(self.place)
        self.place.show()
  
        for i in range(1, 4):
            # On crée un bouton avec un label "Appuyez !"
            bouton = gtk.Button("Appuyez !")
  
           # Lorsque le bouton reçoit le signal "clicked", 
            # il appelle la méthode deplace_bouton().
            bouton.connect("clicked", self.deplace_bouton)
  
            # Ceci place le bouton dans la fenêtre du conteneur
            self.place.put(bouton, i*50, i*50)
  
            # La dernière tâche est d'afficher ce nouveau bouton.
            bouton.show()

        # On affiche la fenêtre
        fenetre.show()

def main():
    # Boucle principale
    gtk.main()
    return 0

if __name__ == "__main__":
    ExemplePlace()
    main()

X-D. Le conteneur Layout (Affiche)

Le conteneur Layout ressemble au conteneur Fixed à ceci près qu'il implémente une zone de défilement infinie (un infini limité à 2^32). Le système X window possède une limitation de taille de fenêtre de 32767x32767 pixels. Le conteneur Layout tourne cette limitation en réalisant des trucs exotiques utilisant la gravité de fenêtre et de bit. Il devient ainsi possible d'obtenir un défilement régulier même lorsque l'on a beaucoup de widgets enfants dans la zone de défilement.

On crée un conteneur Layout par :

 
Sélectionnez
  affiche = gtk.Layout(hadjustment=None, vadjustment=None)

Comme on peut le constater, il est possible d'indiquer, de manière facultative, des objets Adjustment, (voir Chapitre 7, AdjustmentsChapitre 7. Les ajustements) que le widget Layout va utiliser pour son défilement. Si l'on ne précise pas d'objets Adjustment, des nouveaux seront créés automatiquement.

On peut ajouter et déplacer des widgets dans un conteneur Layout avec les méthodes suivantes :

 
Sélectionnez
1.
2.
3.
  affiche.put(widget_enfant, x, y)

  affiche.move(widget_enfant, x, y)

On peut indiquer ou connaître la taille du conteneur Layout en utilisant les méthodes :

 
Sélectionnez
1.
2.
3.
  affiche.set_size(largeur, hauteur)

  taille = layout.get_size()

Ces quatre dernières méthodes du widget Layout permettent de manipuler les widgets d'ajustement horizontal et vertical :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
  horiz_ajust = affiche.get_hadjustment()

  vert_ajust = affiche.get_vadjustment()

  affiche.set_hadjustment(ajustement)

  affiche.set_vadjustment(ajustement)

Le programme layout.py crée trois boutons et les place dans un conteneur Layout "affiche". Quand on clique sur l'un des boutons, il est transféré à un nouvel emplacement, aléatoire, dans le conteneur. La Figure 10.3, « Exemple de conteneur Layout » montre l'affichage de départ.

Image non disponible
Figure 10.3. Exemple de conteneur Layout

Voici le code source du programme layout.py :

 
Sélectionnez
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.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# exemple layout.py

import pygtk
pygtk.require('2.0')
import gtk
import random

class ExempleAffiche:
    def WindowDeleteEvent(self, widget, event):
        # retourne False pour que la fenêtre soit détruite
        return False

    def WindowDestroy(self, widget, *data):
        # quitte la boucle principale
        gtk.main_quit()

    def ButtonClicked(self, bouton):
        # déplace le bouton
        self.affiche.move(bouton, random.randint(0,500),
                         random.randint(0,500))

    def __init__(self):
        # crée la fenêtre de niveau supérieur
        fenêtre = gtk.Window(gtk.WINDOW_TOPLEVEL)
        fenetre.set_title("Exemple conteneur Layout")
        fenetre.set_default_size(300, 300)
        fenetre.connect("delete-event", self.WindowDeleteEvent)
        fenetre.connect("destroy", self.WindowDestroy)
        # crée une table et la place dans la fenêtre
        table = gtk.Table(2, 2, False)
        fenetre.add(table)
        # crée le conteneur affiche et le place dans la table
        self.affiche = gtk.Layout(None, None)
        self.affiche.set_size(600, 600)
        table.attach(self.affiche, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND,
                     gtk.FILL|gtk.EXPAND, 0, 0)
        # crée les barres de défilement et les place dans la table
        vert_defil = gtk.VScrollbar(None)
        table.attach(vert_defil, 1, 2, 0, 1, gtk.FILL|gtk.SHRINK,
                     gtk.FILL|gtk.SHRINK, 0, 0)
        horiz_defil = gtk.HScrollbar(None)
        table.attach(horiz_defil, 0, 1, 1, 2, gtk.FILL|gtk.SHRINK,
                     gtk.FILL|gtk.SHRINK, 0, 0)    
        # les barres de défilement utilisent les ajustements du conteneur
        vAdjust = self.affiche.get_vadjustment()
        vert_defil.set_adjustment(vAdjust)
        hAdjust = self.affiche.get_hadjustment()
        horiz_defil.set_adjustment(hAdjust)
        # crée 3 boutons et les place dans le conteneur 
        bouton = gtk.Button("Clic !")
        bouton.connect("clicked", self.ButtonClicked)
        self.affiche.put(bouton, 0, 0)
        bouton = gtk.Button("Clic !")
        bouton.connect("clicked", self.ButtonClicked)
        self.affiche.put(bouton, 100, 0)
        bouton = gtk.Button("Clic !")
        bouton.connect("clicked", self.ButtonClicked)
        self.affiche.put(bouton, 200, 0)
        # Montre tous les widgets
        fenetre.show_all()

def main():
    # on entre dans la boucle principale
    gtk.main()
    return 0

if __name__ == "__main__":
    ExempleAffiche()
    main()

X-E. Les cadres (Frame)

Les cadres sont utilisés pour englober un ou plusieurs groupes de widgets dans une boite qui peut, éventuellement, avoir une étiquette. La position de l'étiquette et le style de la boite sont modifiables.

(A Frame can be created with the following function:) Un cadre Frame se crée en utilisant :

 
Sélectionnez
  cadre = gtk.Frame(label=None)

où l'étiquette label est placée, par défaut, dans le coin supérieur gauche du cadre. Donner la valeur "None" au paramètre label ou ne pas indiquer de paramètre label entrainera qu'aucune étiquette ne sera affichée. Le texte de l'étiquette est modifiable par :

 
Sélectionnez
  cadre.set_label(label)

Pour modifier la position de l'étiquette, on utilise la méthode :

 
Sélectionnez
  cadre.set_label_align(xalign, yalign)

xalign et yalign ont une valeur comprise entre 0.0 et 1.0. L'argument xalign donne la position de l'étiquette sur le bord horizontal du cadre. Le paramètre yalign n'est pas utilisé actuellement. La valeur par défaut de xalign est 0.0, ce qui place l'étiquette sur le côté gauche du cadre.

La méthode suivante modifie le style de la boite qui délimite le cadre.

 
Sélectionnez
  cadre.set_shadow_type(type)

où le paramètre type peur prendre l'une des valeurs suivantes :

 
Sélectionnez
1.
2.
3.
4.
5.
  SHADOW_NONE
  SHADOW_IN
  SHADOW_OUT
  SHADOW_ETCHED_IN      # par défaut
  SHADOW_ETCHED_OUT

Le programme frame.py montre l'utilisation du widget de cadre Frame. La Figure 10.4, « Exemple de cadre » illustre le résultat obtenu :

Image non disponible
Figure 10.4. Exemple de cadre

Voici le code du programmeframe.py

 
Sélectionnez
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.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# exemple frame.py

import pygtk
pygtk.require('2.0')
import gtk

class ExempleCadre:
    def __init__(self):
        # Créer une nouvelle fenêtre
        fenetre = gtk.Window(gtk.WINDOW_TOPLEVEL)
        fenetre.set_title("Exemple de cadre")

        # On connecte l'événement "destroy" au gestionnaire de signal 
        fenetre.connect("destroy", lambda w: gtk.main_quit())
        fenetre.set_size_request(300, 300)

        # On définit la largeur de bordure de la fenêtre
        fenetre.set_border_width(10)

        # On crée un cadre
        cadre = gtk.Frame()
        fenetre.add(cadre)

        # On indique l'étiquette du cadre
        cadre.set_label("Widget GTK Cadre")

        # On aligne l'étiquette sur la droite du cadre
        cadre.set_label_align(1.0, 0.0)

        # On précise le style du cadre
        cadre.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
        cadre.show()
  
        # On affiche la fenêtre
        fenetre.show()

def main():
    # Enter the event loop
    gtk.main()
    return 0

if __name__ == "__main__":
    ExempleCadre()
    main()

Les programmes calendar.py, label.py et spinbutton.py utilisent aussi des cadres.

X-F. Les cadres proportionnels (AspectFrame)

Le cadre proportionnel est semblable au widget cadre, en outre il maintient le rapport largeur/hauteur du widget enfant à sa valeur en ajoutant de l'espace supplémentaire si nécessaire. Ceci est utile si, par exemple, vous voulez présenter une image plus grande. La taille de la prévisualisation peut varier lorsque l'utilisateur redimensionne la fenêtre, mais les proportions de l'image doivent toujours correspondre à l'image originale.

Pour créer un cadre proportionnel, on utilise :

 
Sélectionnez
  cadre_prop = gtk.AspectFrame(label=None, xalign=0.5, yalign=0.5, ratio=1.0, obey_child=TRUE)

où l'étiquette label indique le texte qui sera affiché, les paramètres xalign et yalign indiquent l'alignement comme décrit dans gtk.Alignment. Si le paramètre obey_child vaut TRUE, les proportions du widget enfant correspondront à la taille idéale qu'il demande, autrement elles seront données par la valeur de ratio.

Pour modifier les arguments d'un cadre proportionnel existant, on utilise :

 
Sélectionnez
  cadre_prop.set(xalign=0.0, yalign=0.0, ratio=1.0, obey_child=TRUE)

En exemple, le programme aspectframe.py utilise un cadre proportionnel AspectFrame pour offrir une zone de dessin dont le rapport de proportions sera toujours de 2/1, quelle que soit la manière dont l'utilisateur redimensionne la fenêtre. La Figure 10.5, « Exemple de cadre proportionnel » montre l'affichage du programme.

Image non disponible
Figure 10.5. Exemple de cadre proportionnel

Voici le code du programme aspectframe.py :

 
Sélectionnez
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.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# exemple aspectframe.py

import pygtk
pygtk.require('2.0')
import gtk

class ExempleAspectFrame:
    def __init__(self):
        fenetre = gtk.Window(gtk.WINDOW_TOPLEVEL);
        fenetre.set_title("Cadre_proportionnel")
        fenetre.connect("destroy", lambda x: gtk.main_quit())
        fenetre.set_border_width(10)

        # Créer un cadre proportionnel - l'ajouter à la fenêtre principale
        cadre_prop = gtk.AspectFrame("2x1", # étiquette
                                       0.5, # x centré dans la fenêtre à
                                       0.5, # y centré dans la fenêtre à
                                       2, # rapport x/y = 2
                                       False) # ignorer taille des widgets enfant
        fenetre.add(cadre_prop)
        cadre_prop.show()

        # Ajouter un widget enfant dans le cadre proportionnel
        zone_dessin = gtk.DrawingArea()

        # Déclarer une fenêtre de 200x200, mais le cadre proportionnel
        # avec un rapport de 2x1 affiche une zone de 200x100
        zone_dessin.set_size_request(200, 200)
        cadre_prop.add(zone_dessin)
        zone_dessin.show()
        fenetre.show()

def main():
    gtk.main()
    return 0

if __name__ == "__main__":
    ExempleAspectFrame()
    main()

X-G. Fenêtre à volets (Paned Window)

Le widget de fenêtre à volets permet de diviser une zone en deux parties, la taille relative de chaque partie est gérée par l'utilisateur. Un sillon, dessiné entre les deux parties, possède une poignée qui permet à l'utilisateur de modifier le partage. La division peut être horizontale (HPaned) ou verticale (VPaned).

Pour créer une nouvelle fenêtre à volets, on appelle :

 
Sélectionnez
1.
2.
3.
  volet_horiz = gtk.HPaned()

  volet_vert = gtk.VPaned()

Après avoir créé la fenêtre à volets, il faut ajouter un widget enfant dans chaque moitié. Pour cela, on utilise les méthodes :

 
Sélectionnez
1.
2.
3.
  volet.add1(enfant)

  volet.add2(enfant)

La méthode add1() ajoute le widget enfant au volet gauche ou supérieur, la méthode add2() ajoute le widget enfant au volet droit ou inférieur de la fenêtre à volets.

Le programme paned.py crée une partie de l'interface utilisateur d'un logiciel de courrier électronique imaginaire. Une fenêtre est divisée en deux parties verticales, la partie supérieure comprend une liste de messages et la partie inférieure le texte du message. La plus grande part du programme est plutôt simple. Deux points sont cependant à noter : on ne peut ajouter de texte à un widget Texte tant qu'il n'est pas réalisé. Ce qui peut être fait en appelant la méthode realize(), mais comme démonstration d'une technique alternative, on connecte un gestionnaire au signal "realize" pour ajouter le texte. Il faudrait aussi ajouter l'option SHRINK à certains des items dans la partie contenant la fenêtre de texte et ses ascenseurs. Ainsi, lorsque la partie basse est réduite, les sections corrigées diminuent au lieu de disparaître par le bas de la fenêtre. La Figure 10.6, « Exemple de fenêtre à volets » illustre le programme en cours de fonctionnement.

Image non disponible
Figure 10.6. Exemple de fenêtre à volets

Voici le code du programme paned.py :

 
Sélectionnez
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.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# example paned.py

import pygtk
pygtk.require('2.0')
import gtk, gobject

class ExempleVolets:
    # Crée la liste des "messages"
    def create_list(self):
        # Crée une nouvelle fenêtre à défilement avec ascenseurs si nécessaire
        fenetre_defil = gtk.ScrolledWindow()
        fenetre_defil.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

        modele = gtk.ListStore(gobject.TYPE_STRING)
        tree_view = gtk.TreeView(modele)
        fenetre_defil.add_with_viewport (tree_view)
        tree_view.show()

        # Ajoute quelques messages dans la fenêtre
        for i in range(10):
            msg = "Message #%d" % i
            iter = modele.append()
            modele.set(iter, 0, msg)

        cellule = gtk.CellRendererText()
        colonne = gtk.TreeViewColumn("Messages", cellule, text=0)
        tree_view.append_column(colonne)

        return fenetre_defil
   
    # Ajoute du texte au widget texte - ceci est un rappel qui est invoqué
    # quand la fenêtre est réalisée. On pourrait forcer aussi la fenêtre à être
    # réalisée avec gtk.Widget.realize(), mais elle devrait appartenir 
    # d'abord à une hiérarchie.
    def insert_text(self, buffer):
        iter = buffer.get_iter_at_offset(0)
        buffer.insert(iter,
                      "From: pathfinder@nasa.gov\n"
                      "To: mom@nasa.gov\n"
                      "Subject: Faites-le !\n"
                      "\n"
                      "Nous sommes arrivés juste ce matin. \n"
                      "Le temps a été superbe, clair, mais froid\n"
                      "et il y a plein de vues amusantes.\n"
                      "Sojourner vous dit bonjour. À bientôt.\n"
                      " -Path\n")
   
    # Crée une zone de texte avec ascenseurs qui affiche un "message"
    def create_text(self):
        vuetexte = gtk.TextView()
        buffer = vuetexte.get_buffer()
        fenetre_defil = gtk.ScrolledWindow()
        fenetre_defil.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        fenetre_defil.add(vuetexte)
        self.insert_text(buffer)
        fenetre_defil.show_all()
        return fenetre_defil
   
    def __init__(self):
        fenêtre = gtk.Window(gtk.WINDOW_TOPLEVEL)
        fenetre.set_title("Fenêtre à volets")
        fenetre.connect("destroy", lambda w: gtk.main_quit())
        fenetre.set_border_width(10)
        fenetre.set_size_request(450, 400)

        # Crée une fenêtre à volets verticale et l'ajoute à la fenêtre principale
        vpaned = gtk.VPaned()
        fenetre.add(vpaned)
        vpaned.show()

        # Crée le contenu des deux moitiés de la fenêtre
        liste = self.create_list()
        vpaned.add1(liste)
        liste.show()

        texte = self.create_text()
        vpaned.add2(texte)
        texte.show()
        fenetre.show()

def main():
    gtk.main()
    return 0

if __name__ == "__main__":
    ExempleVolets()
    main()

X-H. Viewports

Il y a peu de chances que vous ayez besoin d'utiliser directement le widget Viewport. Plus probablement, vous utiliserez le widget ScrolledWindow (voir Section 10.9, « Fenêtre avec barres de défilement (Scrolled Window) »Fenêtre avec barres de défilement (Scrolled Window)) qui lui, utilise Viewport.

Un widget Viewport permet de placer un widget plus grand à l'intérieur de lui-même de sorte qu'il n'est possible de voir qu'une partie de celui-ci à la fois. Il utilise l'objet Adjustment (voir Chapitre 7, AdjustmentsChapitre 7. Les ajustements) pour définir la partie actuellement visible.

On crée un Viewport par la fonction :

 
Sélectionnez
  viewport = gtk.Viewport(hadjustment=None, vadjustment=None)

Comme on peut le constater, on peut préciser lors de la création du widget les objets Adjustment verticaux et horizontaux qu'il devra utiliser. Le widget créera ses propres ajustements si on attribue la valeur None aux arguments ou si on n'utilise pas d'arguments.

Il est possible de retrouver et de définir les ajustements après la création du widget grâce aux quatre méthodes suivantes :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
  viewport.get_hadjustment()

  viewport.get_vadjustment()

  viewport.set_hadjustment(adjustment)

  viewport.set_vadjustment(adjustment)

La seule autre méthode de viewport sert à modifier son apparence :

 
Sélectionnez
  viewport.set_shadow_type(type)

Voici les valeurs possibles pour le paramètre type :

 
Sélectionnez
1.
2.
3.
4.
5.
  SHADOW_NONE           # pas d'ombrage
  SHADOW_IN             # ombrage intérieur
  SHADOW_OUT            # ombrage extérieur
  SHADOW_ETCHED_IN      # ombrage gravé en creux
  SHADOW_ETCHED_OUT     # ombrage gravé en relief

X-I. Fenêtre avec barres de défilement (Scrolled Window)

Les fenêtres avec barres de défilement servent à créer une zone défilante contenant un autre widget. On peut insérer n'importe quel widget dans ces fenêtres, ils seront accessibles, quelle que soit leur taille en utilisant les barres de défilement.

La fonction suivante sert à créer une fenêtre avec barre de défilement :

 
Sélectionnez
  scrolled_window = gtk.ScrolledWindow(hadjustment=None, vadjustment=None)

où le premier paramètre est l'ajustement horizontal, et le second l'ajustement vertical. Ils valent presque toujours None ou ne sont pas indiqués.

 
Sélectionnez
  scrolled_window.set_policy(hscrollbar_policy, vscrollbar_policy)

Cette méthode définit la politique à utiliser par rapport aux barres de défilement. Le premier argument règle la politique pour la barre de défilement horizontale, et le second, la politique pour la barre de défilement verticale.

Cette politique peut être POLICY_AUTOMATIC ou POLICY_ALWAYS. POLICY_AUTOMATIC décidera automatiquement de votre besoin en barres de défilement, alors que POLICY_ALWAYS affichera toujours celles-ci.

On peut ensuite placer un objet dans la fenêtre à défilement grâce à la méthode :

 
Sélectionnez
  scrolled_window.add_with_viewport(child)

Le programme scrolledwin.py place 100 boutons commutateurs dans une fenêtre à défilement. Je n'ai commenté que les parties qui pouvaient vous paraître nouvelles. La Figure 10.7, « Exemple de fenêtre à défilement »« Exemple de fenêtre à défilement »« Exemple de fenêtre à défilement » montre l'affichage du programme :

Image non disponible
Figure 10.7. Exemple de fenêtre à défilement

Voici le code source du programme scrolledwin.py

 
Sélectionnez
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.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# exemple scrolledwin.py

import pygtk
pygtk.require('2.0')
import gtk

class ExempleScrolledWindow:
    def destroy(self, widget):
        gtk.main_quit()

    def __init__(self):
        # Créer une boite de dialogue pour y placer 
        # la fenêtre à défilement 
        fenêtre = gtk.Dialog()
        fenetre.connect("destroy", self.destroy)
        fenetre.set_title("Fenêtre à défilement")
        fenetre.set_border_width(0)
        fenetre.set_size_request(300, 300)

        # Créer une fenêtre à défilement.
        fenetre_defil = gtk.ScrolledWindow()
        fenetre_defil.set_border_width(10)

        # La gestion des barres est soit POLICY AUTOMATIC, soit POLICY_ALWAYS.
        # POLICY_AUTOMATIC décide automatiquement s'il faut ou non des barres,
        # POLICY_ALWAYS met toujours des barres.
        # Le premier paramètre correspond à la barre horizontale,
        # le second à la barre verticale. 
        fenetre_defil.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)

        # Créer une boite de dialogue avec une boite verticale.
        fenetre.vbox.pack_start(fenetre_defil, True, True, 0)
        fenetre_defil.show()
    
        # Créer une table de 10x10 cases.
        table = gtk.Table(10, 10, False)

        # Définir l'espacement des lignes et colonnes à 10 pixels.
        table.set_row_spacings(10)
        table.set_col_spacings(10)

        # Placer la table dans la fenêtre à défilement.
        fenetre_defil.add_with_viewport(table)
        table.show()

        # Créer une grille de boutons commutateurs dans la table
        # pour la démonstration de la fenêtre à défilement
        for i in range(10):
            for j in range(10):
                buffer = "bouton (%d,%d)" % (i, j)
                bouton = gtk.ToggleButton(buffer)
                table.attach(bouton, i, i+1, j, j+1)
                bouton.show()

        # Ajouter un bouton « Fermer » en bas de la boite de dialogue.
        bouton = gtk.Button("Fermer")
        bouton.connect_object("clicked", self.destroy, fenêtre)

        # Définir ce bouton en « bouton par défaut ».
        bouton.set_flags(gtk.CAN_DEFAULT)
        fenetre.action_area.pack_start(bouton, True, True, 0)

        # Récupérer le bouton par défaut. Presser
        # la touche « Entrée » activera le bouton.
        bouton.grab_default()
        bouton.show()
        fenetre.show()

def main():
    gtk.main()
    return 0

if __name__ == "__main__":
    ExempleScrolledWindow()
    main()

Jouez à modifier la taille de la fenêtre et observez les réactions des barres de défilement. On peut aussi utiliser la fonction set_size_request() pour définir la taille par défaut de la fenêtre et des autres widgets.

X-J. Boites à boutons (ButtonBox)

La boite à boutons ButtonBox est un moyen simple de réaliser rapidement un groupe de boutons. Elle peut être horizontale ou verticale. On crée une nouvelle boite à boutons par un des appels suivants, respectivement pour une boite horizontale ou verticale :

 
Sélectionnez
1.
2.
3.
  hboite_bout = gtk.HButtonBox()

  vboite_bout = gtk.VButtonBox()

Les seules méthodes s'appliquant aux boites à boutons concernent leur disposition à l'intérieur de la boite.

La disposition des boutons dans leur boite se fait par :

 
Sélectionnez
  boite_bout.set_layout(layout_style)

Le paramètre layout_style peut prendre une des valeurs suivantes :

 
Sélectionnez
1.
2.
3.
4.
5.
  BUTTONBOX_DEFAULT_STYLE   # par défaut
  BUTTONBOX_SPREAD          # espacé
  BUTTONBOX_EDGE            # collé aux bords
  BUTTONBOX_START           # collé au début
  BUTTONBOX_END             # collé à la fin

Le style d'affichage layout_style peut être retrouvé en utilisant :

 
Sélectionnez
  layout_style = boite_bout.get_layout()

Les boutons sont ajoutés dans la ButtonBox en utilisant la méthode habituelle des Container :

 
Sélectionnez
  boite_bout.add(widget)

Le programme exemple buttonbox.py utilise tous les différents modes de placement des ButtonBoxes. En voici le résultat :

Image non disponible
Exemple de boite à boutons

Voici le code source du programme buttonbox.py :

 
Sélectionnez
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.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# example boutonbox.py

import pygtk
pygtk.require('2.0')
import gtk

class ExempleBoiteBouton:
    # Créer une boite à boutons avec les paramètres indiqués
    def create_boite_bout(self, horizontal, title, spacing, layout):
        cadre = gtk.Frame(title)

        if horizontal:
            boite_bout = gtk.HButtonBox()
        else:
            boite_bout = gtk.VButtonBox()

        boite_bout.set_border_width(5)
        cadre.add(boite_bout)

        # Définir l'apparence de la boite à boutons
        boite_bout.set_layout(layout)
        boite_bout.set_spacing(spacing)

        bouton = gtk.Button(stock=gtk.STOCK_OK)
        boite_bout.add(bouton)

        bouton = gtk.Button(stock=gtk.STOCK_CANCEL)
        boite_bout.add(bouton)

        bouton = gtk.Button(stock=gtk.STOCK_HELP)
        boite_bout.add(bouton)

        return cadre

    def __init__(self):
        fenêtre = gtk.Window(gtk.WINDOW_TOPLEVEL)
        fenetre.set_title("Boites à boutons")

        fenetre.connect("destroy", lambda x: gtk.main_quit())

        fenetre.set_border_width(10)

        boîtev_princ = gtk.VBox(False, 0)
        fenetre.add(boîtev_princ)

        cadre_horz = gtk.Frame("Boites à boutons horizontales")
        boîtev_princ.pack_start(cadre_horz, True, True, 10)

        boitev = gtk.VBox(False, 0)
        boîtev.set_border_width(10)
        cadre_horz.add(boitev)

        boîtev.pack_start(self.create_boite_bout(True, "Spread (spacing 40)",
                                         40, gtk.BUTTONBOX_SPREAD),
                        True, True, 0)

        boîtev.pack_start(self.create_boite_bout(True, "Edge (spacing 30)",
                                         30, gtk.BUTTONBOX_EDGE),
                        True, True, 5)

        boîtev.pack_start(self.create_boite_bout(True, "Start (spacing 20)",
                                         20, gtk.BUTTONBOX_START),
                        True, True, 5)

        boîtev.pack_start(self.create_boite_bout(True, "End (spacing 10)",
                                         10, gtk.BUTTONBOX_END),
                        True, True, 5)

        cadre_vert = gtk.Frame("Boites à boutons verticales")
        boîtev_princ.pack_start(cadre_vert, True, True, 10)

        boiteh = gtk.HBox(False, 0)
        boîteh.set_border_width(10)
        cadre_vert.add(boiteh)

        boîteh.pack_start(self.create_boite_bout(False, "Spread (spacing 5)",
                                         5, gtk.BUTTONBOX_SPREAD),
                        True, True, 0)

        boîteh.pack_start(self.create_boite_bout(False, "Edge (spacing 30)",
                                         30, gtk.BUTTONBOX_EDGE),
                        True, True, 5)

        boîteh.pack_start(self.create_boite_bout(False, "Start (spacing 20)",
                                         20, gtk.BUTTONBOX_START),
                        True, True, 5)

        boîteh.pack_start(self.create_boite_bout(False, "End (spacing 20)",
                                         20, gtk.BUTTONBOX_END),
                        True, True, 5)

        fenetre.show_all()

def main():
    # Entrer dans la boucle principale
    gtk.main()
    return 0

if __name__ == "__main__":
    ExempleBoiteBouton()
    main()

X-K. La barre d'outils (Toolbar)

Les barres d'outils Toolbars sont habituellement utilisées pour grouper des widgets dans le but de faciliter la personnalisation de leur style et de leur disposition. Typiquement une barre d'outils se compose de boutons avec des icônes, des étiquettes et des infobulles, mais n'importe quel autre widget peut aussi être placé dans une barre d'outils. Enfin, les éléments peuvent être disposés horizontalement ou verticalement et les boutons peuvent être affichés avec des icônes, des étiquettes, ou les deux.

Créer une barre d'outils se fait (comme on pourrait s'en douter) de la façon suivante :

 
Sélectionnez
  barre_outil = gtk.Toolbar()

Une fois la barre d'outils créée, on peut y ajouter en début ou en fin, ou y insérer au milieu, des items (du texte simple) ou des éléments (de type widget). Pour décrire un item, il nous faut un texte pour l'étiquette, l'infobulle, un texte privé pour l'infobulle, une icône pour le bouton et une fonction de rappel pour celui-ci. Par exemple, pour ajouter un item en début ou en fin, on peut utiliser la méthode suivante :

 
Sélectionnez
1.
2.
3.
  barre_outil.append_item(text, tooltip_text, tooltip_private_text, icon, callback, user_data=None)

  barre_outil.prepend_item(text, tooltip_text, tooltip_private_text, icon, callback, user_data)

Si l'on souhaite utiliser la méthode insert_item(), le seul paramètre supplémentaire à indiquer est la position à laquelle il faut insérer l'item, ainsi :

 
Sélectionnez
  barre_outil.insert_item(text, tooltip_text, tooltip_private_text, icon, callback, user_data, 
                          position)

Pour simplifier l'ajout d'espaces entre les items de la barre d'outils, on peut utiliser les méthodes suivantes :

 
Sélectionnez
1.
2.
3.
4.
5.
  barre_outil.append_space()

  barre_outil.prepend_space()

  barre_outil.insert_space(position)

Si nécessaire, l'orientation de la barre d'outils, son style et la disponibilité des infobulles peuvent être modifiés "à la volée" en utilisant les méthodes suivantes :

 
Sélectionnez
1.
2.
3.
4.
5.
  barre_outil.set_orientation(orientation)

  barre_outil.set_style(style)

  barre_outil.set_tooltips(enable)

où le paramètre orientation peut avoir pour valeur ORIENTATION_HORIZONTAL ou ORIENTATION_VERTICAL. style sert à définir l'apparence de la barre d'outils grâce aux valeurs TOOLBAR_ICONS, TOOLBAR_TEXT ou TOOLBAR_BOTH. L'argument enable est TRUE soit FALSE.

Pour montrer certaines autres possibilités de la barre d'outils, prenons le programme exemple toolbar.py (nous interromprons le listing avec quelques explications supplémentaires) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# example barre_outils.py

import pygtk
pygtk.require('2.0')
import gtk

class ExempleBarreOutils:
    # Cette méthode est connectée au bouton Fermer ou à la fermeture
    # de la fenêtre depuis le gestionnaire de fenêtre
    def delete_event(self, widget, event=None):
        gtk.main_quit()
        return False

Le début ci-dessus devrait vous sembler familier si ce n'est pas votre premier programme PyGTK. Il y a cependant un élément supplémentaire, nous importons une jolie image XPM (gtk.xpm) pour l'utiliser comme icône pour tous les boutons. La ligne 10 débute la classe ExempleBarreOutils et aux lignes 12-14, on définit la méthode de rappel qui terminera le programme.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
    # Ceci est simple... quand l'un de ces boutons est commuté, 
    # on cherche lequel est actif et on règle le style de la
    # barre d'outils en conséquence.
    def change_radio(self, widget, barre_outils):
        if self.bouton_texte.get_active(): 
            barre_outils.set_style(gtk.TOOLBAR_TEXT)
        elif self.bouton_icone.get_active():
            barre_outils.set_style(gtk.TOOLBAR_ICONS)
        elif self.bouton_deux.get_active():
            barre_outils.set_style(gtk.TOOLBAR_BOTH)

    # Encore plus simple, on vérifie le bouton à bascule et on
    # autorise/interdit les infobulles
    def change_bascule(self, widget, barre_outils):
        barre_outils.set_tooltips(widget.get_active())

Aux lignes 19-30, deux méthodes de rappel qui seront appelées quand on cliquera sur un des boutons de la barre d'outils. Ce genre de choses devrait déjà vous être familier si vous avez déjà utilisé des boutons à bascule (et des boutons radio).

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
    def __init__(self):
        # Ici, la fenêtre principale (un dialogue) et la boite à poignée.
        # Bon, on a besoin d'une barre d'outils, d'une icône avec un masque
        # (un pour tous les boutons) et un widget icône dans lequel la placer
        # (on crée un widget pour chaque bouton)
        # On crée une nouvelle fenêtre avec son titre, d'une taille adaptée
        dialogue = gtk.Dialog()
        dialogue.set_title("Exemple GTKToolbar")
        dialogue.set_size_request(450, 250)
        dialogue.set_resizable(True)

        # En général, on quitte quand quelqu'un essaie de nous fermer
        dialogue.connect("delete_event", self.delete_event)

        # Pour faire les choses bien, nous plaçons la barre d'outils dans une
        # boite à poignées, ainsi elle peut être détachée de la fenêtre principale. 
        boite_poing = gtk.HandleBox()
        dialogue.vbox.pack_start(boite_poing, False, False, 5)

La partie ci-dessus devrait être similaire à toute autre application PyGTK. Simplement une initialisation d'une instance de ExempleBarreOutils, création de la fenêtre, etc. Un élément, peut-être, demande une explication : la boite à poignées. Une boite à poignées est simplement une boite qui peut être utilisée pour y placer des widgets. La différence avec une boite standard est que la précédente peut être détachée d'une fenêtre parent (en réalité, la boite demeure à l'intérieur de son parent, mais est réduite à un tout petit rectangle, pendant que tous ses éléments peuvent être raccordés à une nouvelle fenêtre flottante libre). Habituellement il est pratique d'avoir une barre d'outils détachable, donc ces deux widgets vont souvent ensemble.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
        # La barre d'outils sera horizontale, avec à la fois des icônes 
        # et du texte et un espace de 5 pixels entre les items. Enfin, 
        # nous la mettons dans notre boite à poignées. 
        barre_outils = gtk.Toolbar()
        barre_outils.set_orientation(gtk.ORIENTATION_HORIZONTAL)
        barre_outils.set_style(gtk.TOOLBAR_BOTH)
        barre_outils.set_border_width(5)
        boite_poing.add(barre_outils)

Bon, ce que nous faisons ici est une simple initialisation de la barre d'outils.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
        # le premier item est le bouton "Fermer"
        img_icone = gtk.Image() # widget icône
        img_icone.set_from_file("gtk.xpm")
        bouton_fermer = barre_outils.append_item(
            "Fermer",               # étiquette du bouton
            "Fermer l'application", # infobulle du bouton
            "Privé",                # info privée du bouton
            img_icone,              # widget icône
            self.delete_event)      # un signal
        barre_outils.append_space() # espace après l'item

Dans le code ci-dessus, nous voyons le cas le plus simple : l'ajout d'un bouton à la barre d'outils. Juste avant d'ajouter un nouvel élément nous devons créer un widget image pour servir d'icône pour cet élément ; il faudra répéter cette étape pour chaque nouvel élément. Nous ajoutons aussi un espace juste après l'élément, de façon à ce que les éléments ne soient pas collés les uns aux autres. Comme vous pouvez le voir, la méthode append_item() renvoie une référence au nouveau bouton créé, ainsi on peut l'utiliser de la manière habituelle

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
        # maintenant, on crée le groupe de boutons radio
        img_icone = gtk.Image() # widget icône
        img_icone.set_from_file("gtk.xpm")
        bouton_icone = barre_outils.append_element(
            gtk.TOOLBAR_CHILD_RADIOBUTTON, # type d'élément
            None,                          # widget
            "Icônes",                      # étiquette
            "Barre d'outils avec icônes uniquement",       # infobulle
            "Privé",                       # info privée du bouton
            img_icone,                     # icône
            self.change_radio,             # signal
            barre_outils)                  # donnée pour le signal
        barre_outils.append_space()
        self.bouton_icone = bouton_icone

Ici nous commençons à créer un groupe de boutons radio. Pour ce faire nous utilisons la méthode append_element(). En fait, en utilisant cette méthode on peut aussi ajouter des éléments simples et même des espaces (type = gtk.TOOLBAR_CHILD_SPACE ou gtk.TOOLBAR_CHILD_BUTTON). Dans l'exemple précédent, nous commençons à créer un groupe de boutons radio. Pour créer d'autres boutons radio, une référence au bouton précédent dans ce groupe est exigée, de sorte que l'on puisse construire facilement une liste de boutons (voir la Section 6.4, « Radio Buttons »Les boutons radio). Nous sauvons aussi une référence au bouton dans l'instance de classe ExempleBarreOutils pour y accéder ensuite.

 
Sélectionnez
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.
        # les boutons radio suivants renvoient aux précédents
        img_icone = gtk.Image() # widget icône
        img_icone.set_from_file("gtk.xpm")
        bouton_texte = barre_outils.append_element(
            gtk.TOOLBAR_CHILD_RADIOBUTTON,
            bouton_icone,
            "Texte",
            "Barre d'outils avec texte uniquement",
            "Privé",
            img_icone,
            self.change_radio,
            barre_outils)
        barre_outils.append_space()
        self.bouton_texte = bouton_texte

        img_icone = gtk.Image() # widget icône
        img_icone.set_from_file("gtk.xpm")
        bouton_deux = barre_outils.append_element(
            gtk.TOOLBAR_CHILD_RADIOBUTTON,
            bouton_texte,
            "Les deux",
            "Barre d'outils avec icônes et texte",
            "Privé",
            img_icone,
            self.change_radio,
            barre_outils)
        barre_outils.append_space()
        self.bouton_deux = bouton_deux
        bouton_deux.set_active(True)

Nous créons les autres boutons radio de la même manière en rajoutant le nom de l'un des boutons créés à la méthode append_element() pour indiquer à quel groupe de boutons appartient ce dernier.

Enfin, il faut définir manuellement l'état de l'un des boutons radio (sinon ils seraient tous dans l'état actif, nous interdisant de passer de l'un à l'autre).

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
        # Ici un simple bouton à bascule
        img_icone = gtk.Image() # widget icône
        img_icone.set_from_file("gtk.xpm")
        bouton_info = barre_outils.append_element(
            gtk.TOOLBAR_CHILD_TOGGLEBUTTON,
            None,
            "Infobulles",
            "Barre d'outils avec/sans infobulles",
            "Privé",
            img_icone,
            self.change_bascule,
            barre_outils)
        barre_outils.append_space()
        bouton_info.set_active(True)

Un bouton à bascule peut être créé de la manière habituelle (si l'on sait déjà créer les boutons radio).

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
        # pour placer un élément dans la barre d'outils, il faut
        # seulement le créer et l'ajouter avec une infobulle adéquate
        zone_saisie = gtk.Entry()
        barre_outils.append_widget(zone_saisie,  "Juste une zone de saisie", "Privé")

        # il n'est pas créé dans la barre d'outils, il faut l'afficher
        zone_saisie.show()

On constate qu'ajouter n'importe quel type de widget dans une barre d'outils est simple. La chose à se rappeler est que ce widget doit être affiché manuellement (contrairement aux éléments qui seront affichés ensemble avec la barre d'outils)

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
        # C'est fait ! Affichons tout.
        barre_outils.show()
        boite_poing.show()
        dialogue.show()

def main():
    # On reste dans gtk_main et on attend que la fête commence !
    gtk.main()
    return 0

if __name__ == "__main__":
    ExempleBarreOutils()
    main()

À la ligne 142, on clôt la définition de la classe ExempleBarreOutils. Aux lignes 144-147, on définit la fonction main() qui appelle simplement la fonction gtk.main() pour lancer la boucle de traitement d'événement. Aux lignes 149-151, on crée l'instance de ExempleBarreOutils et ensuite on entre dans la boucle de traitement d'événement. Nous voici à la fin du tutoriel de barre d'outils. Naturellement, pour l'apprécier dans sa totalité, il vous faut aussi cette sympathique icône gtk.xpm. La Figure 10.8, « Exemple de barre d'outils » montre l'affichage obtenu :

Image non disponible
Figure 10.8. Exemple de barre d'outils

X-L. Le bloc-notes (Notebook)

Le bloc-notes Notebook est une collection de "pages" qui se recouvrent ; chaque page contient des informations différentes et une seule de ces pages est visible à la fois. Ce widget est devenu très commun dernièrement dans la programmation d'interface graphique, et c'est un bon moyen de montrer des blocs d'information similaires en garantissant une séparation de leur affichage.

La première fonction dont on aura besoin, comme on pourrait s'en douter, sert à créer un nouveau bloc-notes.

 
Sélectionnez
  bloc_notes = gtk.Notebook()

Une fois le bloc-notes créé, il existe un certain nombre de méthodes qui agissent sur lui. Regardons-les une à une.

La première que nous verrons sert à positionner les indicateurs de pages. Ces indicateurs de pages ou "onglets" comme on les appelle, peuvent être positionnés de quatre manières : en haut, en bas, à gauche ou à droite.

 
Sélectionnez
  bloc_notes.set_tab_pos(pos)

pos peut prendre une des valeurs suivantes (assez claires par elles-mêmes) :

 
Sélectionnez
1.
2.
3.
4.
  POS_LEFT       # à gauche
  POS_RIGHT      # à droite
  POS_TOP        # en haut
  POS_BOTTOM     # en bas

La valeur par défaut est POS_TOP.

Ensuite, voyons comment ajouter des pages au bloc-notes. Il existe trois moyens d'ajouter des pages à un bloc-notes Notebook. Voyons les deux premiers, presque similaires :

 
Sélectionnez
1.
2.
3.
  bloc_notes.append_page(child, tab_label)

  bloc_notes.prepend_page(child, tab_label)

Ces méthodes ajoutent des pages en les insérant à la fin (append) ou au début (prepend) du bloc-notes. Le paramètre child est le widget qui est placé dans la page et le paramètre tab_label est l'étiquette de la page ajoutée. Le widget child doit être créé séparément et consiste en un ensemble de déclarations d'options placées dans un autre widget conteneur, tel qu'une table.

La dernière méthode pour ajouter des pages à un bloc-notes contient toutes les propriétés des deux précédentes, mais permet de préciser la position où l'on veut insérer la page.

 
Sélectionnez
  bloc_notes.insert_page(child, tab_label, position)

Les paramètres sont les mêmes que pour append() et prepend() avec un paramètre supplémentaire, position. Celui-ci indique la position de la page à insérer, la première page possédant la position zéro.

Maintenant que nous savons comment ajouter des pages, voyons comment supprimer une page du bloc-notes.

 
Sélectionnez
  bloc_notes.remove_page(num_page)

Cette méthode supprime du bloc-notes la page indiquée par num_page.

Pour connaître la page courante d'un bloc-notes, on utilise la méthode :

 
Sélectionnez
  page = bloc_notes.get_current_page()

Les deux méthodes suivantes sont de simples appels qui permettent de passer à la page précédente ou à la page suivante. Il suffit de fournir à chaque méthode le bloc-notes sur lequel on veut agir.

 
Sélectionnez
1.
2.
3.
  bloc_notes.next_page()

  bloc_notes.prev_page()

Lorsque le bloc-notes est sur la dernière page et que l'on appelle la méthode next_page(), rien ne se passe. De même, lorsque l'on est sur la première page et que l'on appelle prev_page(), rien ne se passe.

La méthode suivante définit la page "active". Si l'on veut que le bloc-notes s'ouvre à la page 5, par exemple, on utilisera cette méthode. Sinon, le bloc-notes affichera par défaut la première page.

 
Sélectionnez
  bloc_notes.set_current_page(num_page)

Les deux méthodes suivantes ajoutent ou enlèvent respectivement les onglets des pages et les bordures du bloc-notes.

 
Sélectionnez
1.
2.
3.
  bloc_notes.set_show_tabs(montre_onglet)

  bloc_notes.set_show_border(montre_bordure)

La méthode suivante est utile lorsque l'on a un grand nombre de pages, et que tous les onglets ne peuvent pas être affichés. Cela permet de faire défiler les onglets en utilisant deux boutons de navigation.

 
Sélectionnez
  bloc_notes.set_scrollable(defile)

Les paramètres montre_onglet, montre_bordure et defile ont pour valeur TRUE ou FALSE.

Voyons maintenant un exemple. Le programme notebook.py crée une fenêtre avec un bloc-notes et six boutons. Le bloc-notes contient 11 pages, ajoutées selon les trois méthodes différentes, au début, à la fin ou insérées. Les boutons vous permettent de décaler la position des onglets, d'ajouter ou d'enlever les onglets ou les bordures, de supprimer une page, de circuler entre les pages d'avant en arrière et de quitter le programme.

Image non disponible
Figure 10.9. Exemple de bloc-notes

Voici le code source de notebook.py :

 
Sélectionnez
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.
137.
138.
139.
140.
141.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# example bloc_notes.py

import pygtk
pygtk.require('2.0')
import gtk

class ExempleBlocNotes:
    # Cette méthode fait circuler la position des étiquettes
    def circule_bloc(self, bouton, bloc_notes):
        bloc_notes.set_tab_pos((bloc_notes.get_tab_pos()+1) %4)

    # Ajoute/supprime les onglets et bordures des pages
    def style_bloc(self, bouton, bloc_notes):
        val_onglet = False
        val_bord = False
        if self.show_tabs == False:
        val_onglet = True 
        if self.show_border == False:
        val_bord = True

        bloc_notes.set_show_tabs(val_onglet)
        self.show_tabs = val_onglet
        bloc_notes.set_show_border(val_bord)
        self.show_border = val_bord

    # Supprime une page du bloc-notes
    def supprime_bloc(self, bouton, bloc_notes):
        page = bloc_notes.get_current_page()
        bloc_notes.remove_page(page)
        # Il faut rafraîchir l'affichage du widget
        # Ceci oblige le widget à se redessiner
        bloc_notes.queue_draw_area(0,0,-1,-1)

    def delete(self, widget, event=None):
        gtk.main_quit()
        return False

    def __init__(self):
        fenêtre = gtk.Window(gtk.WINDOW_TOPLEVEL)
        fenetre.connect("delete_event", self.delete)
        fenetre.set_border_width(10)

        table = gtk.Table(3,6,False)
        fenetre.add(table)

        # Créer un nouveau bloc-notes, définir la position des onglets
        bloc_notes = gtk.Notebook()
        bloc_notes.set_tab_pos(gtk.POS_TOP)
        table.attach(bloc_notes, 0,6,0,1)
        bloc_notes.show()
        self.show_tabs = True
        self.show_border = True

        # On ajoute quelques pages au bloc-notes
        for i in range(5):
            bufferf = "Ajout après cadre %d" % (i+1)
            bufferl = "PostPage %d" % (i+1)

            cadre = gtk.Frame(bufferf)
            cadre.set_border_width(10)
            cadre.set_size_request(100, 75)
            cadre.show()

            label = gtk.Label(bufferf)
            cadre.add(label)
            label.show()

            label = gtk.Label(bufferl)
            bloc_notes.append_page(cadre, label)
      
        # Ajout d'une page à un endroit précis
        checkbouton = gtk.CheckButton("Cliquez-moi svp !")
        checkbouton.set_size_request(100, 75)
        checkbouton.show ()

        label = gtk.Label("Ajout page")
        bloc_notes.insert_page(checkbouton, label, 2)

        # Enfin, ajout de page au début du bloc-notes
        for i in range(5):
            bufferf = "Ajout avant cadre %d" % (i+1)
            bufferl = "PréPage %d" % (i+1)

            cadre = gtk.Frame(bufferf)
            cadre.set_border_width(10)
            cadre.set_size_request(100, 75)
            cadre.show()

            label = gtk.Label(bufferf)
            cadre.add(label)
            label.show()

            label = gtk.Label(bufferl)
            bloc_notes.prepend_page(cadre, label)
    
        # Définir la page d'ouverture (page 4)
        bloc_notes.set_current_page(3)

        # Créer quelques boutons
        bouton = gtk.Button("fermer")
        bouton.connect("clicked", self.delete)
        table.attach(bouton, 0,1,1,2)
        bouton.show()

        bouton = gtk.Button("page suiv.")
        bouton.connect("clicked", lambda w: bloc_notes.next_page())
        table.attach(bouton, 1,2,1,2)
        bouton.show()

        bouton = gtk.Button("page préc.")
        bouton.connect("clicked", lambda w: bloc_notes.prev_page())
        table.attach(bouton, 2,3,1,2)
        bouton.show()

        bouton = gtk.Button("pos. onglet")
        bouton.connect("clicked", self.circule_bloc, bloc_notes)
        table.attach(bouton, 3,4,1,2)
        bouton.show()

        bouton = gtk.Button("onglet/bords")
        bouton.connect("clicked", self.style_bloc, bloc_notes)
        table.attach(bouton, 4,5,1,2)
        bouton.show()

        bouton = gtk.Button("supp. page")
        bouton.connect("clicked", self.supprime_bloc, bloc_notes)
        table.attach(bouton, 5,6,1,2)
        bouton.show()

        table.show()
        fenetre.show()

def main():
    gtk.main()
    return 0

if __name__ == "__main__":
    ExempleBlocNotes()
    main()

J'espère que ceci vous aidera dans vos créations de blocs-notes pour vos applications PyGTK.

X-M. Les connexions et connecteurs (Plugs et Sockets)

Les Plugs et les Sockets agissent ensemble pour insérer l'interface utilisateur d'un processus dans un autre. Ceci peut aussi être réalisé avec Bonobo.

X-M-1. Les Plugs

Un Plug encapsule une interface utilisateur fournie par une application pour qu'elle puisse être insérée dans l'interface d'une autre application. Le signal "embedded" avertit l'application connectée qu'elle a été insérée dans l'interface utilisateur de l'autre application.

On crée un Plug par la fonction suivante :

 
Sélectionnez
  plug = gtk.Plug(socket_id)

qui crée un nouveau Plug et l'insère dans le Socket identifié par socket_id. Si socket_id vaut OL, le Plug est laissé "déconnecté" et pourra plus tard, être connecté dans un Socket en utilisant la méthode add_id() du Socket.

La méthode de Plug :

 
Sélectionnez
  id = plug.get_id()

retourne l'identifiant de la fenêtre d'un Plug, qui peut être utilisé pour l'insérer dans un Socket en utilisant la méthode add_id() du Socket.

Le programme exemple plug.py illustre l'utilisation d'un Plug :

 
Sélectionnez
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.
#!/usr/bin/python
# -*- coding:utf-8 -*-
import pygtk
pygtk.require('2.0')
import gtk,sys

Wid = 0L
if len(sys.argv) == 2:
    Wid = long(sys.argv[1])

plug = gtk.Plug(Wid)
print "Id de Plug=", plug.get_id()

def embed_event(widget):
    print "Je (", widget, ") viens juste d'être inséré !"

plug.connect("embedded", embed_event)

entry = gtk.Entry()
entry.set_text("salut")
def entry_point(widget):
    print "Vous avez modifié mon texte en '%s'" % widget.get_text()

entry.connect("changed", entry_point)
plug.connect("destroy", lambda w: gtk.main_quit())

plug.add(entry)
plug.show_all()


gtk.main()

Ce programme se lance ainsi :

 
Sélectionnez
  plug.py [windowID]

où le paramètre windowID est l'identifiant d'un Socket où connecter le Plug.

X-M-2. Sockets

Un Socket fournit le widget pour insérer, de manière transparente, un Plug d'une autre application dans votre interface graphique. Une application crée un Socket et transmet l'identifiant de fenêtre de ce widget à une autre application qui alors, crée un Plug en utilisant cet identifiant de fenêtre comme paramètre. Tout widget contenu dans le Plug apparaitra dans la fenêtre de la première application.

L'ID (identifiant) de fenêtre du Socket s'obtient en utilisant la méthode de Socket.get_id(). Avant d'utiliser cette méthode, le Socket doit être réalisé et ajouté à son parent.

Si l'on transmet l'ID de fenêtre d'un Socket à un autre processus qui va créer un Plug dans ce Socket, il faut s'assurer que le widget Socket n'est pas détruit tant que le Plug n'est pas créé.

Quand GTK+ est prévenu que la fenêtre insérée a été détruite, il détruira aussi le Socket. Il faut toujours être préparé à ce que vos sockets soient détruits à n'importe quel moment quand la boucle principale s'exécute. Détruire un Socket entraine aussi la destruction du Plug inséré.

La communication entre un Socket et un Plug suit le protocole XEmbed. Ce protocole a été implémenté dans d'autres toolkits, par exemple Qt, ce qui permet le même niveau d'intégration lorsque l'on incorpore un widget Qt dans GTK ou vice versa.

Création d'un nouveau Socket vide :

 
Sélectionnez
  socket = gtk.Socket()

Le Socket doit être placé dans une fenêtre de premier niveau avant d'invoquer la méthode add_id() :

 
Sélectionnez
  socket.add_id(window_id)

qui ajoute un client XEMBED, tel un Plug, au Socket. Le client peut être dans le même processus ou un processus différent.

Pour insérer un Plug dans un Socket, on peut soit créer le Plug par :

 
Sélectionnez
  plug = gtk.Plug(0L)

et ensuite, transmettre à la méthode add_id() du Socket le nombre renvoyé par la méthode get_id() du Plug :

 
Sélectionnez
  socket.add_id(plug)

soit utiliser la méthode get_id() du Socket :

 
Sélectionnez
    window_id = socket.get_id()

pour obtenir l'ID de fenêtre du socket et ensuite créer le Plug par :

 
Sélectionnez
  plug = gtk.Plug(window_id)

Le Socket doit avoir été déjà ajouté à une fenêtre de niveau supérieur avant cet appel.

Le programme exemple socket.py illustre l'utilisation d'un Socket :

 
Sélectionnez
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.
#!/usr/bin/python
# -*- coding:utf-8 -*-
import string

import pygtk
pygtk.require('2.0')
import gtk,sys

window = gtk.Window()
window.show()

socket = gtk.Socket()
socket.show()
window.add(socket)

print "ID du Socket = ", socket.get_id()
window.connect("destroy", lambda w: gtk.main_quit())

def plugged_event(widget):
    print "Je (", widget, ") viens juste d'avoir un plug inséré !"

socket.connect("plug-added", plugged_event)

if len(sys.argv) == 2:
    socket.add_id(long(sys.argv[1]))

gtk.main()

Pour tester l'exemple, vous pouvez lancer d'abord plug.py :

 
Sélectionnez
  $ python plug.py
  Id de Plug= 39845891

et copier l'ID résultant comme premier argument à socket.py :

 
Sélectionnez
1.
2.
3.
4.
5.
  $ python socket.py 39845891
  Socket ID= 48234523
  ID du Socket =  44040231
  Je ( <gtk.Plug object (GtkPlug) at 0x404057ac> ) viens juste d'être inséré !
  Je ( <gtk.Socket object (GtkSocket) at 0x4040a9b4> ) viens juste d'avoir un plug inséré !

Ou vous pouvez lancer socket.py :

 
Sélectionnez
  $ python socket.py
  ID du Socket =  39845927

et ensuite lancer plug.py, en recopiant L'ID de fenêtre :

 
Sélectionnez
1.
2.
3.
4.
  $ python plug.py
  39845927
  Je ( <gtk.Socket object (GtkSocket) at 0x4040a9b4> ) viens juste d'avoir un plug inséré !
  Id de Plug= 44040195

précédentsommairesuivant
NdT : depuis PyGTK 2.4., il y a aussi gtk.Alignment.set_padding() et gtk.Alignment.get_padding().
NdT : pris dans le manuel de référence gtk.Alignment.

Copyright © 2005 John Finlay. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.