8. Chapitre 8. Les widgets d'intervalle▲
La catégorie des widgets d'intervalle (gtk.Range) inclut l'incontournable barre de défilement ainsi que le "gradateur", un peu moins courant. Bien que l'on utilise généralement ces deux types d'objets à différentes fins, ils sont assez similaires dans leur implémentation et leur fonctionnement. Les widgets d'intervalle ont un jeu d'éléments graphiques commun : la coulisse et le curseur, qui possèdent tous les deux leur propre fenêtre X et qui reçoivent des évènements. Pour faire avancer ou reculer le curseur dans la coulisse on peut le tirer avec la souris ou bien cliquer directement dans la coulisse. Dans ce second cas, et selon le bouton de la souris utilisé, le curseur viendra se positionner à l'endroit du clic ou s'en rapprochera suivant un pas prédéfini.
Comme nous venons de le voir dans le chapitre sur les ajustementsChapitre 7. Les ajustements, tous les widgets d'intervalle sont associés à un gtk.Adjustment, à partir duquel ils calculent la longueur du curseur et sa position dans la coulisse. Lorsque l'utilisateur manipule le curseur, le widget d'intervalle modifie la valeur de l'ajustement.
8-1. HScrollbar et VScrollbar : les barres de défilement▲
Voici les très classiques barres de défilement. Elles ne devraient être employées que pour faire défiler un autre widget, comme une liste, une zone de texte ou une vue orientable (il est même bien plus pratique d'utiliser une fenêtre à défilement dans la plupart des cas). Pour le reste, les gradateurs seront plus appropriés, car plus simples d'utilisation et offrant plus de fonctionnalités.
Les barres de défilement horizontale et verticale sont implémentées dans deux types différents. Il n'y a pas grand-chose à dire à leur sujet. On les crée en appelant l'une de ces méthodes :
2.
3.
barredefil_h =
gtk.HScrollbar
(
adjustment=
None
)
barredefil_v =
gtk.VScrollbar
(
adjustment=
None
)
et voilà tout. L'argument adjustment (ajustement) attend une référence à un ajustementChapitre 7. Les ajustements existant. Si vous ne lui en fournissez pas, la méthode en créera un pour vous, ce qui peut être intéressant si vous voulez transmettre ce nouvel ajustement à la méthode constructeur d'un autre widget, qui se chargera de le configurer pour vous (une zone de texte, par exemple).
8-2. HScale et VScale : les gradateurs▲
Les gradateurs permettent à l'utilisateur de sélectionner et de manipuler une valeur dans un intervalle donné, ceci graphiquement. Ils peuvent servir, par exemple, à ajuster le degré d'agrandissement de l'aperçu d'une image, à contrôler la luminosité d'une couleur, ou encore à spécifier le nombre de minutes d'inactivité avant le lancement d'un économiseur d'écran.
8-2-1. Créer un gradateur▲
Tout comme pour les barres de défilement, il existe un type de widget pour les gradateurs verticaux et un autre pour les horizontaux (la majorité des programmeurs semble préférer ces derniers). Malgré tout, vu qu'ils fonctionnent de la même manière, nous ne les traiterons pas séparément. Les méthodes suivantes créent respectivement un gradateur vertical et un gradateur horizontal :
2.
3.
gradateurv =
gtk.VScale
(
adjustment=
None
)
gradateurh =
gtk.HScale
(
adjustment=
None
)
L'argument adjustment (ajustement) attend une référence à un ajustementChapitre 7. Les ajustements déjà créé avec gtk.Adjustment(). Si vous ne lui en fournissez pas, la méthode en créera un anonyme dont toutes les valeurs vaudront 0.0 (ce qui n'est pas très utile ici). Afin de vous y retrouver, vous pourriez créer un ajustement dont vous fixeriez la page_size à 0.0, de sorte que sa valeur upper corresponde bien à la plus grande valeur que l'utilisateur peut sélectionner (si vous êtes déjà complètement perdu, relisez la section sur les ajustementsChapitre 7. Les ajustements pour mieux comprendre leur rôle, leur processus de création et leur manipulation).
8-2-2. Méthodes et signaux (enfin, au moins méthodes…)▲
Les gradateurs peuvent afficher un nombre représentant leur valeur courante à côté de la coulisse. Cette option, activée par défaut, peut être modifiée avec la méthode :
gradateur.set_draw_value
(
draw_value)
Comme vous l'avez sûrement deviné, draw_value (valeur d'affichage) vaut soit TRUE soit FALSE, chacune de ces deux expressions entraînant les conséquences que l'on peut en attendre.
La valeur affichée par un gradateur est arrondie par défaut au premier chiffre après la virgule, comme la valeur de son ajustementChapitre 7. Les ajustements. Vous pouvez changer ceci avec la méthode suivante :
gradateur.set_digits
(
digits)
où digits est le nombre de décimales. Vous pouvez en demander autant que vous le souhaitez, mais sachez que seules les 13 premières seront réellement affichées.
Enfin, la valeur peut être affichée à différents endroits autour de la coulisse :
gradateur.set_value_pos
(
pos)
L'argument pos peut prendre l'une des valeurs suivantes :
2.
3.
4.
POS_LEFT #
à
gauche
POS_RIGHT #
à
droite
POS_TOP #
au-dessus
POS_BOTTOM #
au-dessous
Si vous placez la valeur le long de la coulisse (au-dessus ou au-dessous d'un gradateur horizontal, par exemple), elle suivra le curseur dans ses déplacements.
8-3. Méthodes communes aux widgets d'intervalle▲
La classe des widgets d'intervalle (gtk.Range) a des mécanismes internes assez compliqués, qui, comme tous ceux des classes "de base", ne sont pas très intéressants à moins de vouloir les bidouiller. De plus, parmi ses méthodes et signaux, la quasi totalité n'est vraiment utile que pour écrire des widgets dérivés. On trouve cependant quelques méthodes utiles qui fonctionnent avec tous les widgets de la classe.
8-3-1. Définir le mode d'actualisation▲
Le "mode d'actualisation" d'un widget d'intervalle précise le moment où, durant sa manipulation par l'utilisateur, le widget devra changer la valeur de son ajustementChapitre 7. Les ajustements et lui faire émettre le signal "value_changed". Les différents modes d'actualisation sont :
UPDATE_CONTINUOUS(continu) |
C'est le mode par défaut. Le signal "value_changed" est émis de manière continue, c'est-à-dire au moindre déplacement du curseur. |
UPDATE_DISCONTINUOUS(discontinu) |
Le signal "value_changed" n'est émis qu'après que le curseur a cessé de bouger et que l'utilisateur a relâché le bouton de la souris. |
UPDATE_DELAYED(différé) |
Le signal "value_changed" est émis lorsque l'utilisateur relâche le bouton de la souris, ou si le curseur cesse de bouger durant un court instant. |
On définit le mode d'actualisation d'un widget d'intervalle en l'indiquant à l'argument policy (mode) de cette méthode :
widget.set_update_policy
(
policy)
8-3-2. Définir et récupérer les ajustements▲
Pour définir et récupérer "à la volée" l'ajustement d'un widget d'intervalle, on utilise naturellement les méthodes :
2.
3.
widget.set_adjustment
(
adjustment)
ajustement =
widget.get_adjustment
(
)
La méthode get_adjustment() renvoie une référence à l'ajustement auquel widget est connecté.
La méthode set_adjustment() ne fait absolument rien si vous lui transmettez l'ajustement que widget utilise déjà, que vous ayez modifié ou non ses valeurs. En revanche, si vous lui passez un nouvel ajustementajustementsChapitre 7. Les ajustements, elle déréférence le précédent s'il existe (voire le détruit), connecte les signaux appropriés au nouveau, puis recalcule la taille et/ou la position du curseur avant de le réafficher si nécessaire. Comme nous l'avons vu dans la section sur les ajustementsChapitre 7. Les ajustements, si vous voulez réutiliser le même, vous devriez lui faire émettre le signal "changed" quand vous modifiez directement ses valeurs :
ajustement.emit
(
"
changed
"
)
8-4. Clavier et souris▲
Tous les widgets d'intervalle de GTK réagissent aux clics de souris plus ou moins de la même manière. En cliquant dans la coulisse avec le premier bouton, on augmente ou diminue la valeur (value) de son ajustement d'un page_increment, et le curseur est déplacé en conséquence. Si l'on y clique avec le deuxième bouton, le curseur est amené directement au point cliqué. Un clic de n'importe quel bouton sur les flèches d'une barre de défilement augmente ou diminue la valeur de son ajustement d'un step_increment.
Les barres de défilement ne peuvent pas avoir le focus, elle n'ont donc aucune connexion directe avec le clavier. Chez les autres widgets d'intervalle, ces connexions (actives, bien sûr, seulement lorsque les widgets ont le focus) sont les mêmes pour les widgets horizontaux et verticaux.
Tous les widgets d'intervalle peuvent être manipulés avec les flèches haut, bas, gauche et droite du clavier, ainsi qu'avec les touches Page précédente et Page suivante. Les flèches déplacent le curseur par step_incr, et Page précédente/Page suivante par page_incr.
L'utilisateur a également la possibilité de placer le curseur directement à l'une ou l'autre des extrémités de la coulisse avec les touches Début et Fin.
8-5. Démonstration des widgets d'intervalle▲
Le programme d'exemple widgetsintervalle.py affiche une fenêtre comprenant trois widgets d'intervalle connectés au même ajustement, ainsi que plusieurs autres widgets par lesquels il est possible de modifier certains des paramètres vus plus haut ou à la section sur les ajustements. Ceci vous permettra de vous faire une idée plus précise de ce que ces paramètres contrôlent dans le widget. La figure 8.1 montre ce que l'on obtient après avoir lancé le programme :
Le code source de widgetsintervalle.py est le suivant :
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.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
#
!/usr/bin/env
python
#
exemple
widgetsintervalle.py
import
pygtk
pygtk.require
(
'
2
.
0
'
)
import
gtk
#
Ces
fonctions
sont
là
pour
nous
faciliter
les
choses
def
nouvelle_entree
(
nom, rappel, donnees=
None
):
entree =
gtk.MenuItem
(
nom)
entree.connect
(
"
activate
"
, rappel, donnees)
entree.show
(
)
return
entree
def
gradateurs_valeurs_defaut
(
gradateur):
gradateur.set_update_policy
(
gtk.UPDATE_CONTINUOUS)
gradateur.set_digits
(
1
)
gradateur.set_value_pos
(
gtk.POS_TOP)
gradateur.set_draw_value
(
True
)
class
WidgetsIntervalle:
def
modif_position
(
self, entree, position):
#
Fixe
la
position
de
la
valeur
pour
les
deux
gradateurs
self.gradateurh.set_value_pos
(
position)
self.gradateurv.set_value_pos
(
position)
def
modif_mode_actu
(
self, entree, mode):
#
Fixe
le
mode
d'actualisation
pour
les
deux
gradateurs
self.gradateurh.set_update_policy
(
mode)
self.gradateurv.set_update_policy
(
mode)
def
modif_decimales
(
self, ajust):
#
Fixe
le
nombre
de
décimales
pour
arrondir
la
valeur
de
ajust
self.gradateurh.set_digits
(
ajust.value)
self.gradateurv.set_digits
(
ajust.value)
def
modif_taille_page
(
self, ajust2, ajust1):
#
Les
valeurs
"taille
de
la
page"
et
"incrémentation
par
page"
de
l'ajustement
#
exemple
prennent
la
valeur
donnée
par
le
gradateur
"Taille
de
la
page".
ajust1.page_size =
ajust2.value
ajust1.page_incr =
ajust2.value
#
Puis
on
fait
émettre
le
signal
"changed"
à
ajust1
pour
reconfigurer
tous
les
#
widgets
qui
lui
sont
attachés
ajust1.emit
(
"
changed
"
)
def
affiche_valeur
(
self, bouton):
#
Active
ou
desactive
l'affichage
de
la
valeur
sur
les
gradateurs,
#
en
fonction
de
l'état
du
bouton
à
bascule
self.gradateurh.set_draw_value
(
bouton.get_active
(
))
self.gradateurv.set_draw_value
(
bouton.get_active
(
))
#
Construction
de
notre
fenêtre
exemple
def
__init__
(
self):
#
Creation
standard
d'une
fenêtre
self.fenetre =
gtk.Window (
gtk.WINDOW_TOPLEVEL)
self.fenetre.connect
(
"
destroy
"
, gtk.main_quit)
self.fenetre.set_title
(
"
Widgets
d
'
intervalle
"
)
boite1 =
gtk.VBox
(
False
, 0
)
self.fenetre.add
(
boite1)
boite1.show
(
)
boite2 =
gtk.HBox
(
False
, 10
)
boite2.set_border_width
(
10
)
boite1.pack_start
(
boite2, True
, True
, 0
)
boite2.show
(
)
#
valeur,
minimale,
maximale,
incr/pas,
incr/page,
taille
de
la
page
#
Notez
que
le
paramètre
"taille
de
la
page"
n'a
d'effet
qu'avec
les
#
barres
de
défilement,
la
plus
haute
valeur
étant
alors
égale
à
#
"maximale"
moins
"taille
de
la
page"
ajust1 =
gtk.Adjustment
(
0.0
, 0.0
, 101.0
, 0.1
, 1.0
, 1.0
)
self.gradateurv =
gtk.VScale
(
ajust1)
gradateurs_valeurs_defaut
(
self.gradateurv)
boite2.pack_start
(
self.gradateurv, True
, True
, 0
)
self.gradateurv.show
(
)
boite3 =
gtk.VBox
(
False
, 10
)
boite2.pack_start
(
boite3, True
, True
, 0
)
boite3.show
(
)
#
On
réutilise
le
même
ajustement
self.gradateurh =
gtk.HScale
(
ajust1)
self.gradateurh.set_size_request
(
200
, 30
)
gradateurs_valeurs_defaut
(
self.gradateurh)
boite3.pack_start
(
self.gradateurh, True
, True
, 0
)
self.gradateurh.show
(
)
#
On
réutilise
encore
le
même
ajustement
barredefil =
gtk.HScrollbar
(
ajust1)
#
Notez
que
l'on
demande
a
ce
que
les
gradateurs
soient
toujours
#
actualisés
en
continu
quand
la
barre
de
défilement
est
manipulée
barredefil.set_update_policy
(
gtk.UPDATE_CONTINUOUS)
boite3.pack_start
(
barredefil, True
, True
, 0
)
barredefil.show
(
)
boite2 =
gtk.HBox
(
False
, 10
)
boite2.set_border_width
(
10
)
boite1.pack_start
(
boite2, True
, True
, 0
)
boite2.show
(
)
#
Un
bouton
à
bascule
pour
afficher
ou
pas
la
valeur
bouton =
gtk.CheckButton
(
"
Afficher
la
valeur
sur
les
gradateurs
"
)
bouton.set_active
(
True
)
bouton.connect
(
"
toggled
"
, self.affiche_valeur)
boite2.pack_start
(
bouton, True
, True
, 0
)
bouton.show
(
)
boite2 =
gtk.HBox
(
False
, 10
)
boite2.set_border_width
(
10
)
#
Un
menu
déroulant
pour
changer
la
position
de
la
valeur
etiquette =
gtk.Label
(
"
Position
de
la
valeur
:
"
)
boite2.pack_start
(
etiquette, False
, False
, 0
)
etiquette.show
(
)
menuderoul =
gtk.OptionMenu
(
)
menu =
gtk.Menu
(
)
entree =
nouvelle_entree (
"
Au
-
dessus
"
, self.modif_position, gtk.POS_TOP)
menu.append
(
entree)
entree =
nouvelle_entree (
"
Au
-
dessous
"
, self.modif_position,
gtk.POS_BOTTOM)
menu.append
(
entree)
entree =
nouvelle_entree (
"
A
gauche
"
, self.modif_position, gtk.POS_LEFT)
menu.append
(
entree)
entree =
nouvelle_entree (
"
A
droite
"
, self.modif_position, gtk.POS_RIGHT)
menu.append
(
entree)
menuderoul.set_menu
(
menu)
boite2.pack_start
(
menuderoul, True
, True
, 0
)
menuderoul.show
(
)
boite1.pack_start
(
boite2, True
, True
, 0
)
boite2.show
(
)
boite2 =
gtk.HBox
(
False
, 10
)
boite2.set_border_width
(
10
)
#
Encore
un
autre
menu
déroulant,
cette
fois-ci
pour
le
mode
#
d'actualisation
des
gradateurs
etiquette =
gtk.Label
(
"
Mode
d
'
actualisation
:
"
)
boite2.pack_start
(
etiquette, False
, False
, 0
)
etiquette.show
(
)
menuderoul =
gtk.OptionMenu
(
)
menu =
gtk.Menu
(
)
entree =
nouvelle_entree
(
"
Continu
"
, self.modif_mode_actu,
gtk.UPDATE_CONTINUOUS)
menu.append
(
entree)
entree =
nouvelle_entree (
"
Discontinu
"
, self.modif_mode_actu,
gtk.UPDATE_DISCONTINUOUS)
menu.append
(
entree)
entree =
nouvelle_entree (
"
Differe
"
, self.modif_mode_actu,
gtk.UPDATE_DELAYED)
menu.append
(
entree)
menuderoul.set_menu
(
menu)
boite2.pack_start
(
menuderoul, True
, True
, 0
)
menuderoul.show
(
)
boite1.pack_start
(
boite2, True
, True
, 0
)
boite2.show
(
)
boite2 =
gtk.HBox
(
False
, 10
)
boite2.set_border_width
(
10
)
#
Création
d'un
gradateur
horizontal
pour
choisir
le
nombre
de
#
décimales
dans
les
gradateurs
exemples.
etiquette =
gtk.Label
(
"
Decimales
:
"
)
boite2.pack_start
(
etiquette, False
, False
, 0
)
etiquette.show
(
)
ajust2 =
gtk.Adjustment
(
1.0
, 0.0
, 5.0
, 1.0
, 1.0
, 0.0
)
ajust2.connect
(
"
value_changed
"
, self.modif_decimales)
gradateur =
gtk.HScale
(
ajust2)
gradateur.set_digits
(
0
)
boite2.pack_start
(
gradateur, True
, True
, 0
)
gradateur.show
(
)
boite1.pack_start
(
boite2, True
, True
, 0
)
boite2.show
(
)
boite2 =
gtk.HBox
(
False
, 10
)
boite2.set_border_width
(
10
)
#
Un
dernier
gradateur
horizontal
pour
choisir
la
taille
de
la
page
#
de
la
barre
de
défilement.
etiquette =
gtk.Label
(
"
Taille
de
la
page
de
la
\n
barre
de
defilement
:
"
)
boite2.pack_start
(
etiquette, False
, False
, 0
)
etiquette.show
(
)
ajust2 =
gtk.Adjustment
(
1.0
, 1.0
, 101.0
, 1.0
, 1.0
, 0.0
)
ajust2.connect
(
"
value_changed
"
, self.modif_taille_page, ajust1)
gradateur =
gtk.HScale
(
ajust2)
gradateur.set_digits
(
0
)
boite2.pack_start
(
gradateur, True
, True
, 0
)
gradateur.show
(
)
boite1.pack_start
(
boite2, True
, True
, 0
)
boite2.show
(
)
separateur =
gtk.HSeparator
(
)
boite1.pack_start
(
separateur, False
, True
, 0
)
separateur.show
(
)
boite2 =
gtk.VBox
(
False
, 10
)
boite2.set_border_width
(
10
)
boite1.pack_start
(
boite2, False
, True
, 0
)
boite2.show
(
)
bouton =
gtk.Button
(
"
Quitter
"
)
bouton.connect_object
(
"
clicked
"
, gtk.main_quit, self.fenetre)
boite2.pack_start
(
bouton, True
, True
, 0
)
bouton.set_flags
(
gtk.CAN_DEFAULT)
bouton.grab_default
(
)
bouton.show
(
)
self.fenetre.show
(
)
def
main
(
):
gtk.main
(
)
return
0
if
__name__
=
=
"
__main__
"
:
WidgetsIntervalle
(
)
main
(
)
Vous avez peut-être remarqué que le programme n'appelle pas la méthode connect() pour l'évènement "delete_event", mais seulement pour le signal "destroy", et pourtant tout se passe très bien. Ceci est dû au fait qu'un évènement "delete_event" non traité, déclenche automatiquement l'émission du signal "destroy" sur la fenêtre.