XXI. Chapitre 21. Gérer les sélections▲
XXI-A. 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 chaine (sur un affichage donné). Certains atomes sont prédéterminés par le serveur X et par GTK.
XXI-B. 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 chaines 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ées 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 chaine 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 chaine de caractères.
PyGTK organise toutes les données reçues dans une chaine de caractères. Ceci rend facile la manipulation des cibles chaines. Pour récupérer les autres types (par exemple, ATOM ou INTEGER), le programme doit extraire l'information de la chaine 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 chaine 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 le contenu chaine 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 boite à é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.
XXI-C. 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 chaines de caractères, donc le paramètre data doit être inclus dans une chaine Python, mais le format sera de la dimension appropriée (par exemple 32 pour les atomes et les nombres entiers, 8 pour les chaines). Les modules Python struct ou StringIO peuvent être utilisés pour convertir des données non-chaine en chaine de caractères. Par exemple, vous pouvez convertir une liste de nombres entiers vers une chaine et renseigner le selection_data par :
La méthode suivante établit les données de la sélection à partir de la chaine 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 boite 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 chaine 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 chaine unique, elle ne doit pas 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 boite é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
(
)