21.3. Proporcionar la Selección

Proporcionar la selección es un poco más complicado. Se deben registrar los manejadores que se llamarán cuando se solicite la selección. Para cada par selección-objetivo a manejar, se debe hacer una llamada a:

  widget.selection_add_target(selection, target, info)
    

widget (control), selection (selección), y target (objetivo) identifican las peticiones que gestionará este manejador. Cuando llegue una petición para una selección, se llamará a la señal "selection_get". info es un entero que se puede usar como identificador para el objetivo específico dentro de la retrollamada.

La retrollamada tiene la siguiente signatura:

  def selection_get(widget, selection_data, info, time):
    

El argumento gtk.SelectionData es el mismo que antes, pero en esta ocasión se deben rellenar los campos type, format y data. (El campo format es importante aquí, ya que el servidor X lo usa para saber si el campo data necesita que se le cambie el orden de sus bytes o no. Normalmente será 8 - un caracter - ó 32 - un entero). Esto se hace llamando al método:

  selection_data.set(type, format, data)
    

Este método de PyGTK sólo puede tratar datos de cadenas de caracteres, por lo que data debe cargarse en una cadena Python pero format tendrá el tamaño apropiado para los datos (por ejemplo 32 para átomos y enteros, 8 para cadenas). Los módulos Python struct o StringIO pueden servir para convertir datos que no son cadenas de caracteres a cadenas de caracteres. Por ejemplo, se puede convertir una lista de enteros en una cadena y ponerlos en el campo selection_data así:

  ilist = [1, 2, 3, 4, 5]

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

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

El siguiente método fija los datos de la selección a partir de dicha cadena:

  selection_data.set_text(str, len)

Cuando la usuaria lo solicite, se debe reclamar la posesión de la selección llamando a:

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

result será TRUE (verdadero) si el programa reclama la selección selection con éxito. Si otra aplicación reclama la posesión de selection, entonces llegará un evento "selection_clear_event".

Como ejemplo de proporcionar la selección, el programa setselection.py añade la funcionalidad de selección a un botón biestado que está dentro de una gtk.EventBox. (Se necesita una gtk.Eventbox porque la selección debe asociarse a una gtk.gdk.Window y un gtk.Button es un control sin ventana en GTK+ 2.0.). Cuando se pulsa el botón biestado el programa reclama la selección primaria. El único objetivo soportado (aparte de algunos objetivos que proporciona la propia GTK+ como "TARGETS"), es el objetivo "STRING". Al solicitar este objetivo se devuelve una representación en cadena de caracteres de la hora actual. La figura Figura 21.2, “Ejemplo de Fijar la Selección” muestra la ventana del programa cuando éste ha conseguido la posesión de la selección primaria:

Figura 21.2. Ejemplo de Fijar la Selección

Ejemplo de Fijar la Selección

El código fuente de setselection.py es:

    1    #!/usr/bin/env python
    2
    3    # ejemplo setselection.py
    4
    5    import pygtk
    6    pygtk.require('2.0')
    7    import gtk
    8    import time
    9
   10    class SetSelectionExample:
   11        # Retrollamada cuando la usuaria conmuta la selección
   12        def selection_toggled(self, widget, window):
   13            if widget.get_active():
   14                self.have_selection = window.selection_owner_set("PRIMARY")
   15                # si la solicitud de la selección falla, se devuelve el botón al
   16                # estado inactivo
   17                if not self.have_selection:
   18                    widget.set_active(gtk.FALSE)
   19            else:
   20                if self.have_selection:
   21                    # No es posible "liberar" la selección en PyGTK
   22                    # simplemente se señala que no se posee
   23                    self.have_selection = gtk.FALSE
   24            return
   25
   26        # Llamada cuando otra aplicación reclama la selección
   27        def selection_clear(self, widget, event):
   28            self.have_selection = gtk.FALSE
   29            widget.set_active(gtk.FALSE)
   30            return gtk.TRUE
   31
   32        # Proporciona la hora actual como selección
   33        def selection_handle(self, widget, selection_data, info, time_stamp):
   34            current_time = time.time()
   35            timestr = time.asctime(time.localtime(current_time))
   36
   37            # Al devolver una cadena única no debe terminar en valor nulo
   38            # Esto se hace automáticamente
   39            selection_data.set_text(timestr, len(timestr))
   40            return
   41
   42        def __init__(self):
   43            self.have_selection = gtk.FALSE
   44            # Creamos la ventana principal
   45            window = gtk.Window(gtk.WINDOW_TOPLEVEL)
   46            window.set_title("Set Selection")
   47            window.set_border_width(10)
   48            window.connect("destroy", lambda w: gtk.main_quit())
   49            self.window = window
   50            # Creamos una caja de eventos que contenga el botón, ya que no tiene su
   51            # propia gtk.gdk.Window
   52            eventbox = gtk.EventBox()
   53            eventbox.show()
   54            window.add(eventbox)
   55
   56            # Creamos un botón biestado que actúe como selección
   57            selection_button = gtk.ToggleButton("Claim Selection")
   58            eventbox.add(selection_button)
   59
   60            selection_button.connect("toggled", self.selection_toggled, eventbox)
   61            eventbox.connect_object("selection_clear_event", self.selection_clear,
   62                                    selection_button)
   63
   64            eventbox.selection_add_target("PRIMARY", "STRING", 1)
   65            eventbox.selection_add_target("PRIMARY", "COMPOUND_TEXT", 1)
   66            eventbox.connect("selection_get", self.selection_handle)
   67            selection_button.show()
   68            window.show()
   69
   70    def main():
   71        gtk.main()
   72        return 0
   73
   74    if __name__ == "__main__":
   75        SetSelectionExample()
   76        main()