21. Chapitre 21. Gérer les sélections▲
21-1. Généralités▲
Les sélections sont un des types d'inter-processus compris par X et GTK+. Une sélection identifie un morceau de données, par exemple, une portion de texte, choisi par l'utilisateur, en glissant avec la souris. Une seule application par affichage (variable display de X), l'application propriétaire peut posséder une sélection déterminée à un moment donné, donc lorsqu'une sélection est réclamée par une application, le précédent propriétaire doit signifier à l'utilisateur que la sélection a été cédée. Les autres applications peuvent demander le contenu d'une sélection sous différentes formes, appelées cibles. Il peut y avoir plusieurs sélections mais la plupart des applications X n'en utilisent qu'une, le sélection primaire.
La plupart du temps, il n'est pas nécessaire qu'une application PyGTK gère elle-même les sélections. Les widgets classiques, tel le widget Entry (voir la Section 9.9, « Les champs de saisie »Les champs de saisie) possèdent déjà la capacité de réclamer la sélection si nécessaire (par exemple, quand l'utilisateur glisse la souris sur le texte), et de récupérer le contenu de la sélection possédée par un autre widget ou une autre application (par exemple, lorsque l'utilisateur clique sur le bouton 2 de la souris). Cependant, il peut exister des circonstances dans lesquelles on souhaite donner à d'autres widgets la capacité de fournir la sélection, ou on souhaite récupérer des cibles normalement non disponibles.
Un concept fondamental pour comprendre la gestion des sélections est celui d'atome (atom). Un atome est un nombre entier qui identifie de façon unique une chaîne (sur un affichage donné). Certains atomes sont prédéterminés par le serveur X et par GTK.
21-2. Récupérer la sélection▲
Récupérer la sélection est un processus asynchrone. Pour démarrer le processus, on appelle :
result =
widget.selection_convert
(
selection, target, time=
0
)
Ceci convertit la sélection selection au format indiqué par le paramètre cible target. La selection est un atome conforme au type de sélection ; les sélections courantes sont les chaînes de caractères :
PRIMARY
SECONDARY
Si c'est possible, le champ temps time devrait être le moment où se produit l'événement qui a déclenché la sélection. Cela aide à s'assurer que les événements se produisent dans l'ordre dans lequel l'utilisateur les a demandés. Cependant, si ce n'est pas disponible (par exemple, si la conversion a été déclenchée par un signal "clicked"), on peut utiliser la valeur 0 qui indique l'utilisation du moment actuel. Le paramètre result vaut TRUE si la conversion réussit, FALSE dans le cas contraire.
Quand le propriétaire de la sélection répond à la requête, un signal "selection_received" est envoyé à votre application. Le gestionnaire de ce signal reçoit un objet gtk.SelectionData qui possède les attributs suivants :
selection
target
type
format
data
Les paramètres selection et target sont les valeurs que l'on a donné dans la méthode selection_convert().
Le paramètre type est un atome qui identifie le type de données renvoyées par le propriétaire de la sélection. Quelques-unes des valeurs possibles sont "STRING", une chaîne de caractères en latin-1, "ATOM", une série d'atomes, "INTEGER", un nombre entier, "image/x-xpixmap", etc. la plupart des cibles ne peuvent renvoyer qu'un seul type.
Voici la liste des atomes standards dans X et GTK+ :
PRIMARY
SECONDARY
ARC
ATOM
BITMAP
CARDINAL
COLORMAP
CURSOR
CUT_BUFFER0
CUT_BUFFER1
CUT_BUFFER2
CUT_BUFFER3
CUT_BUFFER4
CUT_BUFFER5
CUT_BUFFER6
CUT_BUFFER7
DRAWABLE
FONT
INTEGER
PIXMAP
POINT
RECTANGLE
RESOURCE_MANAGER
RGB_COLOR_MAP
RGB_BEST_MAP
RGB_BLUE_MAP
RGB_DEFAULT_MAP
RGB_GRAY_MAP
RGB_GREEN_MAP
RGB_RED_MAP
STRING
VISUALID
WINDOW
WM_COMMAND
WM_HINTS
WM_CLIENT_MACHINE
WM_ICON_NAME
WM_ICON_SIZE
WM_NAME
WM_NORMAL_HINTS
WM_SIZE_HINTS
WM_ZOOM_HINTS
MIN_SPACE
NORM_SPACE
MAX_SPACE END_SPACE,
SUPERSCRIPT_X
SUPERSCRIPT_Y
SUBSCRIPT_X
SUBSCRIPT_Y
UNDERLINE_POSITION
UNDERLINE_THICKNESS
STRIKEOUT_ASCENT
STRIKEOUT_DESCENT
ITALIC_ANGLE
X_HEIGHT
QUAD_WIDTH
WEIGHT
POINT_SIZE
RESOLUTION
COPYRIGHT
NOTICE
FONT_NAME
FAMILY_NAME
FULL_NAME
CAP_HEIGHT
WM_CLASS
WM_TRANSIENT_FOR
CLIPBOARD
Le paramètre format fournit la longueur des unités (par exemple, les caractères) en bits. En général, on ne s'en soucie pas lorsqu'on reçoit les données.
Le paramètre data représente les données renvoyées sous la forme d'une chaîne de caractères.
PyGTK organise toutes les données reçues dans une chaîne de caractères. Ceci rend facile la manipulation des cibles chaînes. Pour récupérer les autres types (par exemple, ATOM ou INTEGER), le programme doit extraire l'information de la chaîne renvoyée. PyGTK fournit deux méthodes pour récupérer le texte et une liste de cibles à partir des données de la sélection.
2.
3.
text =
selection_data.get_text
(
)
targets =
selection_data.get_targets
(
)
où le paramètre text est une chaîne de caractères qui contient le texte de la sélection et targets est une liste des cibles reconnues par la sélection.
Étant donné une gtk.SelectionData contenant une liste de cibles, la méthode :
has_text =
selection_data.targets_include_text
(
)
doit renvoyer TRUE si une ou plusieurs cibles peuvent fournir un texte.
Le programme getselection.py illustre la récupération de cibles "STRING" ou "TARGETS" à partir de la sélection primaire et écrit les données correspondantes sur la console quand on clique sur le bouton associé. La Figure 21.1, « Exemple de récupération de sélection » montre un aperçu du programme :
Voici le code du programme getselection.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.
#
!/usr/bin/env
python
#
-*-
coding:utf-8
-*-
#
exemple
getselection.py
import
pygtk
pygtk.require
(
'
2
.
0
'
)
import
gtk
class
GetSelectionExample:
#
Gestionnaire
de
signal
invoqué
quand
l'utilisateur
#
clique
sur
le
bouton
"Contenu
de
la
cible"
def
get_stringtarget
(
self, widget):
#
Et
demande
la
cible
"STRING"
pour
la
sélection
primaire
ret =
widget.selection_convert
(
"
PRIMARY
"
, "
STRING
"
)
return
#
Gestionnaire
de
signal
invoqué
quand
l'utilisateur
clique
sur
le
bouton
"Liste
des
cibles"
def
get_targets
(
self, widget):
#
Et
demande
la
cible
"TARGETS"
pour
la
sélection
primaire
ret =
widget.selection_convert
(
"
PRIMARY
"
, "
TARGETS
"
)
return
#
Gestionnaire
de
signal
appelé
quand
le
propriétaire
de
la
sélection
retourne
les
données
def
selection_received
(
self, widget, selection_data, data):
#
On
s'assure
que
les
données
sont
au
bon
format
if
str(
selection_data.type) =
=
"
STRING
"
:
#
On
affiche
la
chaîne
reçue
print
"
STRING
TARGET
:
%s
"
%
selection_data.get_text
(
)
elif
str(
selection_data.type) =
=
"
ATOM
"
:
#
On
affiche
la
liste
des
cibles
que
l'on
reçoit
targets =
selection_data.get_targets
(
)
for
target in
targets:
name =
str(
target)
if
name !
=
None
:
print
"
%s
"
%
name
else
:
print
"
(
mauvaise
cible
)
"
else
:
print
"
La
sélection
n
'
est
pas
un
\"
STRING
\"
ou
un
\"
ATOM
\"
!
"
return
False
def
__init__
(
self):
#
Création
de
la
fenêtre
de
niveau
supérieur
window =
gtk.Window
(
gtk.WINDOW_TOPLEVEL)
window.set_title
(
"
Get
Selection
"
)
window.set_border_width
(
10
)
window.connect
(
"
destroy
"
, lambda
w: gtk.main_quit
(
))
vbox =
gtk.VBox
(
False
, 0
)
window.add
(
vbox)
vbox.show
(
)
#
Création
du
bouton
pour
obtenir
la
contenu
chaîne
de
la
cible
button =
gtk.Button
(
"
Contenu
de
la
cible
"
)
eventbox =
gtk.EventBox
(
)
eventbox.add
(
button)
button.connect_object
(
"
clicked
"
, self.get_stringtarget, eventbox)
eventbox.connect
(
"
selection_received
"
, self.selection_received)
vbox.pack_start
(
eventbox)
eventbox.show
(
)
button.show
(
)
#
Création
du
bouton
pour
obtenir
les
formats
de
cible
acceptés
button =
gtk.Button
(
"
Liste
des
cibles
"
)
eventbox =
gtk.EventBox
(
)
eventbox.add
(
button)
button.connect_object
(
"
clicked
"
, self.get_targets, eventbox)
eventbox.connect
(
"
selection_received
"
, self.selection_received)
vbox.pack_start
(
eventbox)
eventbox.show
(
)
button.show
(
)
window.show
(
)
def
main
(
):
gtk.main
(
)
return
0
if
__name__
=
=
"
__main__
"
:
GetSelectionExample
(
)
main
(
)
Les lignes 30-38 se chargent d'obtenir les données de sélection des "TARGETS" et impriment la liste des noms de cibles. Les boutons sont inclus dans leur propre boîtes à évènements, car une sélection doit être associée à un gtk.gdkWindow alors que les boutons sont des widgets sans fenêtre dans GTK+2.0.
21-3. Alimenter la sélection▲
Alimenter la sélection est un peu plus compliqué. Il faut enregistrer des gestionnaires que l'on appellera lorsque la sélection sera réclamée. Pour chaque paire de sélection/cible à gérer, on fait un appel à :
widget.selection_add_target
(
selection, target, info)
où les paramètres widget, selection et target identifient les requêtes dont doit s'occuper ce gestionnaire. Quand une demande pour une sélection est reçue, on appellera le signal "selection_get". Le paramètre info est un nombre entier qui peut être utilisé comme un identifiant pour la cible spécifique dans le rappel.
La fonction de rappel a la forme :
def
selection_get
(
widget, selection_data, info, time):
Le gtk.SelectionData est le même que précédemment, mais cette fois-ci, on est chargé de remplir les champs type, format et data. Le paramètre format est important ici - le serveur X l'utilise pour savoir si data nécessite un changement de l'ordre de ses bytes ou non. Habituellement la valeur est 8, c'est-à-dire un caractère, ou 32 pour un entier). Ceci est réalisé en appelant la méthode :
Cette méthode PyGTK peut seulement manipuler des chaînes de caractères, donc le paramètre data doit être inclus dans une chaîne Python, mais le format sera de la dimension appropriée (par exemple 32 pour les atomes et les nombres entiers, 8 pour les chaînes). Les modules Python struct ou StringIO peuvent être utilisés pour convertir des données non-chaîne en chaîne de caractères. Par exemple, vous pouvez convertir une liste de nombres entiers vers une chaîne et renseigner le selection_data par :
La méthode suivante établit les données de la sélection à partir de la chaîne indiquée :
À la demande de l'utilisateur, on réclame la propriété de la sélection en appelant :
result =
widget.selection_owner_set
(
selection, time=
0
L)
où result vaut TRUE si le programme a réclamé la sélection selection avec succès. Si une autre application réclame la possession de la sélection, on obtiendra une réponse "selection_clear_event".
Comme exemple de fourniture de sélection, le programme setselection.py ajoute une fonction de sélection à un bouton interrupteur compris dans une boîte gtk.EventBox. Le gtk.Eventbox est nécessaire, car la sélection doit être associée à une gtk.gdk.Window alors que le gtk.Button est un widget "sans fenêtre" dans GTK+ 2.0. Quand le bouton interrupteur est enfoncé, le programme réclame la propriété de la sélection primaire. La seule cible reconnue (à part certaines cibles comme "TARGETS" fournies par GTK+ lui-même) est la cible "STRING". Lorsque cette cible est demandée, une représentation en chaîne de caractères du temps actuel est renvoyée. La Figure 21.2, « Exemple de création de sélection » montre le programme au moment où il prend possession de la sélection primaire.
Voici le code du programme setselection.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.
#
!/usr/bin/env
python
#
-*-
coding:utf-8
-*-
#
exemple
setselection.py
import
pygtk
pygtk.require
(
'
2
.
0
'
)
import
gtk
import
time
class
SetSelectionExample:
#
Fonction
de
rappel
quand
l'utilisateur
modifie
la
sélection.
def
selection_toggled
(
self, widget, window):
if
widget.get_active
(
):
self.have_selection =
window.selection_owner_set
(
"
PRIMARY
"
)
#
si
réclamer
la
sélection
échoue,
on
remet
le
bouton
#
dans
l'état
inactif.
if
not
self.have_selection:
widget.set_active
(
False
)
else
:
if
self.have_selection:
#
Impossible
de
libérer
la
sélection
en
PyGTK,
#
on
indique
juste
que
l'on
ne
la
possède
pas.
self.have_selection =
False
return
#
Appelé
lorsqu'une
autre
application
réclame
la
sélection.
def
selection_clear
(
self, widget, event):
self.have_selection =
False
widget.set_active
(
False
)
return
True
#
Fournit
le
temps
actuel
comme
sélection.
def
selection_handle
(
self, widget, selection_data, info, time_stamp):
current_time =
time.time
(
)
timestr =
time.asctime
(
time.localtime
(
current_time))
#
Quand
on
renvoie
une
chaîne
unique,
elle
ne
doit
se
terminer
#
par
une
valeur
nulle.
Ceci
le
fait
pour
nous.
selection_data.set_text
(
timestr, len(
timestr))
return
def
__init__
(
self):
self.have_selection =
False
#
Création
de
la
fenêtre
de
niveau
supérieur
window =
gtk.Window
(
gtk.WINDOW_TOPLEVEL)
window.set_title
(
"
Set
Selection
"
)
window.set_border_width
(
10
)
window.connect
(
"
destroy
"
, lambda
w: gtk.main_quit
(
))
self.window =
window
#
Création
d'une
boîte
évènement
pour
contenir
le
bouton
#
car
il
n'a
pas
sa
propre
GdkWindow.
eventbox =
gtk.EventBox
(
)
eventbox.show
(
)
window.add
(
eventbox)
#
Création
d'un
bouton
interrupteur
pour
agir
avec
la
sélection
selection_button =
gtk.ToggleButton
(
"
Réclamer
sélection
"
)
eventbox.add
(
selection_button)
selection_button.connect
(
"
toggled
"
, self.selection_toggled, eventbox)
eventbox.connect_object
(
"
selection_clear_event
"
, self.selection_clear,
selection_button)
eventbox.selection_add_target
(
"
PRIMARY
"
, "
STRING
"
, 1
)
eventbox.selection_add_target
(
"
PRIMARY
"
, "
COMPOUND_TEXT
"
, 1
)
eventbox.connect
(
"
selection_get
"
, self.selection_handle)
selection_button.show
(
)
window.show
(
)
def
main
(
):
gtk.main
(
)
return
0
if
__name__
=
=
"
__main__
"
:
SetSelectionExample
(
)
main
(
)