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 :
boite_evenement =
gtk.EventBox
(
)
Un widget enfant peut être ajouté à la boite à événement par :
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.
Voici le code source du programme eventbox.py :
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 :
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 :
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
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.
Voici le code du programme fixed.py :
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 :
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 :
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 :
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 :
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.
Voici le code source du programme layout.py :
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 :
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 :
cadre.set_label
(
label)
Pour modifier la position de l'étiquette, on utilise la méthode :
cadre.set_label_align
(
xalign, yalign)
où 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.
cadre.set_shadow_type
(
type)
où le paramètre type peur prendre l'une des valeurs suivantes :
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 :
Voici le code du programmeframe.py
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 :
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 :
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.
Voici le code du programme aspectframe.py :
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 :
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 :
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.
Voici le code du programme paned.py :
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 :
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 :
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 :
viewport.set_shadow_type
(
type)
Voici les valeurs possibles pour le paramètre type :
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 :
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.
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 :
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 :
Voici le code source du programme scrolledwin.py
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 :
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 :
boite_bout.set_layout
(
layout_style)
Le paramètre layout_style peut prendre une des valeurs suivantes :
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 :
layout_style =
boite_bout.get_layout
(
)
Les boutons sont ajoutés dans la ButtonBox en utilisant la méthode habituelle des Container :
boite_bout.add
(
widget)
Le programme exemple buttonbox.py utilise tous les différents modes de placement des ButtonBoxes. En voici le résultat :
Voici le code source du programme buttonbox.py :
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 :
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 :
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 :
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 :
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 :
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) :
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.
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).
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.
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.
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
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.
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).
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).
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)
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 :
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.
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.
bloc_notes.set_tab_pos
(
pos)
pos peut prendre une des valeurs suivantes (assez claires par elles-mêmes) :
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 :
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.
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.
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 :
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.
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.
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.
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.
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.
Voici le code source de notebook.py :
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 :
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 :
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 :
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 =
0
L
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 :
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 :
socket =
gtk.Socket
(
)
Le Socket doit être placé dans une fenêtre de premier niveau avant d'invoquer la méthode add_id() :
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 :
plug =
gtk.Plug
(
0
L)
et ensuite, transmettre à la méthode add_id() du Socket le nombre renvoyé par la méthode get_id() du Plug :
socket.add_id
(
plug)
soit utiliser la méthode get_id() du Socket :
window_id =
socket.get_id
(
)
pour obtenir l'ID de fenêtre du socket et ensuite créer le Plug par :
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 :
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 :
$ python plug.py
Id de Plug=
39845891
et copier l'ID résultant comme premier argument à socket.py :
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 :
$ python socket.py
ID du Socket =
39845927
et ensuite lancer plug.py, en recopiant L'ID de fenêtre :
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