IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Apprendre à utiliser le module Python PyGTK 2.0


précédentsommairesuivant

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 :

 
Sélectionnez
  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 :

 
Sélectionnez
  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 :

 
Sélectionnez
  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+ :

 
Sélectionnez
  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.

 
Sélectionnez
1.
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 :

 
Sélectionnez
  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  :

Image non disponible
Figure 21.1. Exemple de récupération de sélection

Voici le code du programme getselection.py :

 
Sélectionnez
1.
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 à :

 
Sélectionnez
  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 :

 
Sélectionnez
  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 :

 
Sélectionnez
  selection_data.set(type, format, data)

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 :

 
Sélectionnez
1.
2.
3.
4.
5.
  ilist = [1, 2, 3, 4, 5]

  data = apply(struct.pack, ['%di'%len(ilist)] + ilist)

  selection_data.set("INTEGER", 32, data)

La méthode suivante établit les données de la sélection à partir de la chaine indiquée :

 
Sélectionnez
  selection_data.set_text(str, len)

À la demande de l'utilisateur, on réclame la propriété de la sélection en appelant :

 
Sélectionnez
  result = widget.selection_owner_set(selection, time=0L)

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.

Image non disponible
Figure 21.2. Exemple de création de sélection

Voici le code du programme setselection.py :

 
Sélectionnez
1.
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()

précédentsommairesuivant

Copyright © 2005 John Finlay. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.