10. Chapitre 10. Les widgets conteneurs▲
10-1. La boîte à é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 boîte à évènement EventBox.
À première vue, la boîte à é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 performance,s 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 boîte à évènement EventBox, faire :
boite_evenement =
gtk.EventBox
(
)
Un widget enfant peut être ajouté à la boîte à évènement par :
event_box.add
(
widget)
Le programme d'exemple eventbox.py illustre les deux utilisations de la boîte à évènement. Créer un label, inscrit dans une petite boîte, 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 boîte à é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):
fenetre =
gtk.Window
(
gtk.WINDOW_TOPLEVEL)
fenetre.set_title
(
"
Boîte
à
évènement
"
)
fenetre.connect
(
"
destroy
"
, lambda
w: gtk.main_quit
(
))
fenetre.set_border_width
(
10
)
#
On
crée
une
boîte
à
é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
boîte
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
(
)
10-2. 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.
10-3. 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
(
)
10-4. 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
fenetre =
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
(
)
10-5. Les cadres (Frame)▲
Les cadres sont utilisés pour englober un ou plusieurs groupes de widgets dans une boîte qui peut, éventuellement, avoir une étiquette. La position de l'étiquette et le style de la boîte 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 entraînera 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 coté gauche du cadre.
La méthode suivante modifie le style de la boîte qui délimite le cadre.
cadre.set_shadow_type
(
type)
où le paramètre type peur prendre l'un 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.
10-6. 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, quel 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
(
)
10-7. 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):
fenetre =
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
(
)
10-8. 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
10-9. 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 » 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
boîte
de
dialogue
pour
y
placer
#
la
fenêtre
à
défilement
fenetre =
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
boîte
de
dialogue
avec
une
boîte
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
boîte
de
dialogue.
bouton =
gtk.Button
(
"
Fermer
"
)
bouton.connect_object
(
"
clicked
"
, self.destroy, fenetre)
#
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.
10-10. Boîtes à boutons (ButtonBox)▲
La boîte à 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 boîte à boutons par un des appels suivants, respectivement pour une boîte horizontale ou verticale :
2.
3.
hboite_bout =
gtk.HButtonBox
(
)
vboite_bout =
gtk.VButtonBox
(
)
Les seules méthodes s'appliquant aux boîtes à boutons concernent leur disposition à l'intérieur de la boîte.
La disposition des boutons dans leur boîte 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
boîte
à
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
boîte
à
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):
fenetre =
gtk.Window
(
gtk.WINDOW_TOPLEVEL)
fenetre.set_title
(
"
Boîtes
à
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
(
"
Boîtes
à
boutons
horizontales
"
)
boîtev_princ.pack_start
(
cadre_horz, True
, True
, 10
)
boîtev =
gtk.VBox
(
False
, 0
)
boîtev.set_border_width
(
10
)
cadre_horz.add
(
boîtev)
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
(
"
Boîtes
à
boutons
verticales
"
)
boîtev_princ.pack_start
(
cadre_vert, True
, True
, 10
)
boîteh =
gtk.HBox
(
False
, 0
)
boîteh.set_border_width
(
10
)
cadre_vert.add
(
boîteh)
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
(
)
10-11. 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
boîte
à
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
chose
bien,
nous
plaçons
la
barre
d'outils
dans
une
#
boîte
à
poignée,
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 boîte à poignée. Une boîte à poignée est simplement une boîte qui peut être utilisée pour y placer des widgets. La différence avec une boîte standard est que la précédente peut être détachée d'une fenêtre parent (en réalité, la boîte 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
boîte
à
poignée.
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ément 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 :
10-12. 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é 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):
fenetre =
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 bloc-notes pour vos applications PyGTK.
10-13. 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.
10-13-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.
10-13-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 apparaîtra 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 entraîne 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