Mostrando entradas con la etiqueta GTK. Mostrar todas las entradas
Mostrando entradas con la etiqueta GTK. Mostrar todas las entradas

19 de mayo de 2016

54. GTK: señales, llamadas y botones

Señales y llamadas

Visión general (literal de la página Señales y retornos de llamada)
Al igual que la mayoría de los kits de herramientas de GUI, GTK+ usa un modelo de programación dirigida por eventos. Cuando el usuario no hace nada, GTK+ se queda en el bucle principal y espera la entrada. Si el usuario realiza alguna acción (como una pulsación del ratón), entonces el bucle principal «despierta» y le envía un evento a GTK+.
Cuando los widgets reciben un evento, frecuentemente emiten una o más señales. Las señales le notifican a su programa que «algo interesante sucedió» invocando funciones que les ha conectado. Estas funciones se conocen comúnmente como retornos de llamada. Cuando sus retornos de llamada se invoquen, típicamente realizaría alguna acción. Después de que un retorno de llamada termina, GTK+ retorna al bucle principal y espera más entradas del usuario.
Un ejemplo genérico es: id_de_manejador = widget.connect("evento", retorno_de_llamada, datos). widget es una instancia de un widget que creó anteriormente. A continuación, el evento en el que está interesado. Cada widget tiene sus propios eventos particulares que pueden ocurrir. Por ejemplo, si tiene un «Gtk.Button» generalmente querrá conectar el evento «clicked»: esto significa que cuando el botón se pulsa, la señal se envía. Otro ejemplo es la señal notify::property: cada vez que una propiedad se modifica en un «GObject», en lugar de sólo emitir la señal notify, GObject asocia como un detalle a la emisión de la señal el nombre de la propiedad modificada. Esto permite clientes que quieran notificarse de cambios de una sola propiedad para filtrar la mayoría de los eventos antes de recibirlos. Tercero, el argumento «retorno_de_llamada» es el nombre de la función de retorno de llamada, que contiene el código que se ejecuta cuando se emiten las señales del tipo especificado. Finalmente, el argumento opcional «datos» incluye cualquier dato que debe pasarse cuando se emita la señal.
La función devuelve un número (el id_de_manejador) que identifica este par «señal-retorno de llamada» particular. Este número se requiere para desconectar una señal para que la función de retorno de llamada no se llame durante emisiones de la señal a la que se ha conectado futuras o en progreso, como en widget.disconnect(id_de_manejador).
Referencias
Señales en la documentación de GObject
Conceptos básicos: bucle principal y señales en el tutorial de GTK+ en Python 3

Muchos, muchos botones...

Ejemplo con muchos botones
En el siguiente programa presentamos una pequeña funcionalidad: tres bombillas apagadas, que se pueden encender/apagar mediante una serie de botones. Simplemente, copia el código, prueba y estudia cómo se han implantado cada uno de los botones.
# coding: utf-8
from gi.repository import Gtk
import sys, random

class miventana(Gtk.ApplicationWindow):

    def __init__(self, app):
        Gtk.Window.__init__(self, title="Ejemplo con botones", application=app)
        self.set_default_size(450, 350)
    self.set_border_width(10)
 
        #2.- definir objetos separadores, y las imágenes
        sepHorizontal = [] 
    imagen = []
    chbtn = [] # checkbuttons
    for i in xrange(0,3):
        sepHorizontal.append(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL))
        imagen.append(Gtk.Image())
        imagen[i].set_from_file("bombilla_off.png")
        chbtn.append(Gtk.CheckButton())
        chbtn[i].set_label("Botón nº "+str(i+1))
        chbtn[i].set_active(False)
        chbtn[i].connect("toggled", self.si_check, i, imagen) # detectar señal de cambio en los checkbuttons
    
    # 3.- Tres radio buttons, conectados en grupo con new_from_widget
    rdbtn1 = Gtk.RadioButton(label="Botón nº 1")
    rdbtn1.connect("toggled",self.si_rdcheck, imagen)
    rdbtn1.set_active(False)
    rdbtn2 = Gtk.RadioButton.new_from_widget(rdbtn1)
    rdbtn2.set_label("Botón nº 2")
    rdbtn2.connect("toggled",self.si_rdcheck, imagen)
    rdbtn2.set_active(False)
    rdbtn3 = Gtk.RadioButton.new_with_label_from_widget(rdbtn1, "Botón nº 3")
    rdbtn3.connect("toggled",self.si_rdcheck, imagen)
    rdbtn3.set_active(False)
    
    # 4.- Un toggle - button para encenderlos a todos
    tglbtn = Gtk.ToggleButton(label="Enciende")
    tglbtn.set_active(False)
    tglbtn.connect("toggled",self.tglconnect, imagen)
    
    # 5.- Un interruptor, que enciende aleatoriamente una de ellas
    sw = Gtk.Switch()
    sw.connect("notify::active", self.swconnect, imagen)
    
    # 6.- Y se me olvida..., ¡un botón normal...!
    icono = Gtk.Image() # un objeto imagen nuevo
    icono.set_from_file("bombilla_on.png") # una imagen del almacén
    btn = Gtk.Button()
    btn.set_image(icono)
    # btn.set_label("Todas")
    btn.connect("clicked",self.conectar,imagen)
    
    #1.- definir objeto rejilla
        rejilla = Gtk.Grid()
        rejilla.set_column_spacing(20)
    
    #3.- definir los grid attach
    for i in xrange(0,3):
        rejilla.attach(imagen[i],i,0,1,1)
    rejilla.attach(sepHorizontal[0],0,1,3,1) # una separación
    for i in xrange(0,3):
        rejilla.attach(chbtn[i],i,2,1,1) # check buttons
    rejilla.attach(sepHorizontal[1],0,3,3,1) # una separación
    rejilla.attach(rdbtn1,0,4,1,1)
    rejilla.attach(rdbtn2,1,4,1,1)
    rejilla.attach(rdbtn3,2,4,1,1)
    rejilla.attach(sepHorizontal[2],0,5,3,1) # una separación
    rejilla.attach(tglbtn,0,6,1,1) # toggle
    rejilla.attach(sw,1,6,1,1) # switch
    rejilla.attach(btn,2,6,1,1) # botón normal
    
    rejilla.set_column_homogeneous(True) # para centrar los objetos en la columna
    rejilla.set_row_homogeneous(True) # para centrar los objetos en las filas
        self.add(rejilla)
    
    # =================================================================
    # Funciones de llamada
    # =================================================================
    
    # llamada si activan/desactivan los checkbuttons    
    def si_check(self, button, i, imagen):
    # importante, pasarle la lista imagen...
    if button.get_active():
        print "Encendido", i
        imagen[i].set_from_file("bombilla_on.png")
    else:
        print "Apagado", i
        imagen[i].set_from_file("bombilla_off.png")
        
    # llamada si activan/desactivan los radiobuttons   
    def si_rdcheck(self, button, imagen):
      if button.get_active(): # la que está activa
          valor = button.get_label()
          k = int(valor[-1])
          self.borrar(imagen) # borra el resto de imágenes
          imagen[k-1].set_from_file("bombilla_on.png")
          
    def tglconnect(self, button, imagen):
    if button.get_active():
        button.set_label("Apaga")
        self.todos(imagen)
    else:
        button.set_label("Enciende")
        self.borrar(imagen)
        
    def swconnect(self, button, active, imagen): 
    # switch button pasa ya tres atributos self, button y active
    i = random.randint(0,2)
    # print i, button.get_active()
    self.borrar(imagen)
    if button.get_active():
        imagen[i].set_from_file("bombilla_on.png")
          
    def conectar(self,button,imagen):
    self.todos(imagen)

    # =================================================================
    # Otras llamadas
    # =================================================================

    def borrar(self, imagen):
    for i in xrange(0,3):
        imagen[i].set_from_file("bombilla_off.png")

    def todos(self, imagen):
    for i in xrange(0,3):
        imagen[i].set_from_file("bombilla_on.png")

# =================================================================
# Mi aplicación
# =================================================================

class miaplicacion(Gtk.Application):

    def __init__(self):
        Gtk.Application.__init__(self)

    def do_activate(self):
        win = miventana(self)
        win.show_all()

    def do_startup(self):
        Gtk.Application.do_startup(self)

app = miaplicacion()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
= = =
Ampliando...
  • De la clase GtkButton
    • set_relief(Gtk.ReliefStyle.NONE) o bien, Gtk.ReliefStyle.NORMAL para cambiar estilo del relieve del botón.
    • Imagen desde el almacén
      • image = Gtk.Image()
        image.set_from_stock(Gtk.STOCK_ABOUT, Gtk.IconSize.BUTTON)
        button.set_image(image)
    • set_focus_on_click(False), para que cuando se pulse el botón no atrape al foco. Así se puede pulsar sin abandonar, por ejemplo, un área de texto.
  • GtkRadioButton
  • GtkSwitch
El último tipo de botón que nos queda es el que permite acceder a una página web (GtkLinkButton). Completar el programa con...
...
    enlace = Gtk.LinkButton(uri="https://es.wikipedia.org/wiki/Digital")
    enlace.set_label("Enlace a una web")
...
    rejilla.attach(enlace,0,7,3,1) # enlace
...
= = =

Enlace a carpeta para el programa botones.


53. GTK: elementos para posicionar

Rejillas (Grid)

Posicionar los diferentes widgets dentro de una ventana implica usar widgets que, a su vez, dividen las ventanas en zonas. En el siguiente ejemplo, usamos GtkGrid, o rejilla, cuyo método más fundamental es grid.attach(). En él, debemos indicar que widget "hijo" (child) vamos a posicionar dentro del grid. Y los siguientes parámetros son left, top, width, height.
  • left: posición de la columna empezando por cero.
  • top: posición de la fila, empezando por cero
  • width: el "hijo" ocupará tantas celdas de ancho
  • height: el hijo ocupará tantas celdas de alto.
Otro método a tener en cuenta es grid.attach_next_to() en el que colocamos nuestro "hijo" al lado de otro ya existente. El primer parámetro es el nuevo "hijo" a posicionar, el segundo el "pariente" que ya existe, el tercero el lado a colocar en referencia al pariente (Gtk.PositionType.BOTTOM, por ejemplo) y después quedan width y height como en el caso anterior. También se puede usar LEFT, RIGHT y TOP.
Un ejemplo, lo tenemos en:
from gi.repository import Gtk
import sys


class MyWindow(Gtk.ApplicationWindow):

    def __init__(self, app):
        Gtk.Window.__init__(self, title="Grid Example", application=app)

        # three labels
        label_top_left = Gtk.Label(label="This is Top Left")
        label_top_right = Gtk.Label(label="This is Top Right")
        label_bottom = Gtk.Label(label="This is Bottom")

        # a grid
        grid = Gtk.Grid()

        # some space between the columns of the grid
        grid.set_column_spacing(20)

        # in the grid:
        # attach the first label in the top left corner
        grid.attach(label_top_left, 0, 0, 1, 1)
        # attach the second label
        grid.attach(label_top_right, 1, 0, 1, 1)
        # attach the third label below the first label
        grid.attach_next_to(label_bottom, label_top_left, Gtk.PositionType.BOTTOM, 2, 1)

        # add the grid to the window
        self.add(grid)


class MyApplication(Gtk.Application):

    def __init__(self):
        Gtk.Application.__init__(self)

    def do_activate(self):
        win = MyWindow(self)
        win.show_all()

app = MyApplication()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
= = =
Otro ejemplo, que establece una rejilla de 10x10
from gi.repository import Gtk
import sys


class MyWindow(Gtk.ApplicationWindow):

    def __init__(self, app):
        Gtk.Window.__init__(self, title="Grid Example", application=app)
        
        tope = 10
        
        # muchas etiquetas
        labels=[]
        for i in xrange(0,tope):
            for j in xrange(0,tope):
                labels.append(Gtk.Label(label="Etiqueta "+str(i)+":"+str(j)))


        # a grid
        grid = Gtk.Grid()

        # some space between the columns of the grid
        grid.set_column_spacing(5)

        # 
        for i in xrange(0,tope):
            for j in xrange(0,tope):
                grid.attach(labels[i+j*10],i,j,1,1)
        

        # add the grid to the window
        self.add(grid)


class MyApplication(Gtk.Application):

    def __init__(self):
        Gtk.Application.__init__(self)

    def do_activate(self):
        win = MyWindow(self)
        win.show_all()

app = MyApplication()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
Otros métodos útiles (copiado del tutorial)
  • insert_row(posición) e insert_column(posición) hacen que los hijos añadidos en esa posición o por debajo se muevan una fila hacia abajo, y que los hijos que se extienden a lo largo de esta posición crezcan para ocupar la siguiente fila. insert_next_to(hermano, lado) inserta una fila o columna en la posición especificada. La fila o columna nueva se pone al lado del hermano, a su lado; si es Gtk.PositionType.TOP or Gtk.PositionType.BOTTOM, se inserta una fila; si es Gtk.PositionType.LEFT o Gtk.PositionType.RIGHT, se inserta una columna.
  • set_row_homogeneous(True) y set_column_homogeneous(True) se aseguran de que (respectivamente) cada fila o cada columna tengan la misma anchura o altura.
  • set_row_spacing(espaciado) y set_column_spacing(espaciado) fuerzan un espaciado entre (respectivamente) filas o columnas. El valor de espaciado puede estar entre 0, que es el valor predeterminado, y 32767.

Separadores

Los separadores son elementos como los otros, que se colocan, preferentemente, en una rejilla. Creo que un simple estudio del ejemplo será suficiente (GtkLabel):
# coding: utf-8
from gi.repository import Gtk
import sys


class miventana(Gtk.ApplicationWindow):

    def __init__(self, app):
        Gtk.Window.__init__(self, title="Ejemplo de separador", application=app)

        # Las etiquetas
        label1 = Gtk.Label()
        label1.set_text("Abajo, un separador horizontal.")

        label2 = Gtk.Label()
        label2.set_text("A la derecha, un separador vertical.")

        label3 = Gtk.Label()
        label3.set_text("A la izquierda, un separador vertical")

        # separador horizontal        
        hseparator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL, width=2)
        # separador vertical
        vseparator = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL)

        # una rejilla donde incrustar los separadores
        grid = Gtk.Grid()
        grid.attach(label1, 0, 0, 3, 1)
        grid.attach(hseparator, 0, 1, 3, 1)
        grid.attach(label2, 0, 2, 1, 1)
        grid.attach(vseparator, 1, 2, 1, 1)
        grid.attach(label3, 2, 2, 1, 1)
        grid.set_column_homogeneous(True)
        # añado a la ventana la rejilla.
        self.add(grid)


class miaplicacion(Gtk.Application):

    def __init__(self):
        Gtk.Application.__init__(self)

    def do_activate(self):
        win = miventana(self)
        win.show_all()

app = miaplicacion()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
= = =

Ventanas con scroll

Las ventanas con scroll son aquellas que presentan barras verticales u horizontales ( o ambas) en las que se puede navegar por su contenido desplazándolas. En el ejemplo (elegir una imagen mayor que la ventana) (GtkScrolledWindow):
# coding: utf-8
from gi.repository import Gtk
import sys

class miventana(Gtk.ApplicationWindow):

    def __init__(self, app):
        Gtk.Window.__init__(self, title="Ejemplo de scroll", application=app)
        self.set_default_size(5, 5)

        # La ventana con scroll
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.set_border_width(10)
        #política de scroll: que siempre aparezca
        scrolled_window.set_policy(
            Gtk.PolicyType.ALWAYS, Gtk.PolicyType.ALWAYS)

        # una imagen más grande que la ventana
        image = Gtk.Image()
        image.set_from_file("coche.png")

        # la añade a la ventana con scroll
        scrolled_window.add_with_viewport(image)

        # y la ventana con scroll a la ventana en sí
        self.add(scrolled_window)


class miaplicacion(Gtk.Application):

    def __init__(self):
        Gtk.Application.__init__(self)

    def do_activate(self):
        win = miventana(self)
        win.show_all()

app = miaplicacion()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
= = =

Métodos útiles para un widget «ScrolledWindow» (del tutorial)
  • set_policy(norma_barra_despl_h, norma_barra_despl_v) donde cada uno de los argumentos puede ser Gtk.Policy.AUTOMATIC, Gtk.Policy.ALWAYS, Gtk.Policy.NEVER. Regulan si deben las aparecer barras de desplazamiento horizontal y vertical: con AUTOMATIC sólo aparecen si se necesitan, con ALWAYS aparecen siempre, y con NEVER no aparecen nunca.
  • add_with_viewport(widget) se usa para añadir el widget «Gtk.Widget» sin capacidad de desplazamiento nativo dentro de la ventana.
  • set_placement(lugar_de_ventana) establece la ubicación del contenido con respecto a las barras de desplazamiento de la ventana. Las opciones para el argumento son Gtk.CornerType.TOP_LEFT (predeterminada: las barras de desplazamiento están en la parte inferior y derecha de la ventana), Gtk.CornerType.TOP_RIGHT, Gtk.CornerType.BOTTOM_LEFT, Gtk.CornerType.BOTTOM_RIGHT.
  • set_hadjustment(ajuste) y set_vadjustment(ajuste) establecen el ajuste «Gtk.Adjustment». Este es la representación de un valor con un límite superior e inferior, junto con pasos y páginas de incrementos, y un tamaño de página, y se construye como Gtk.Adjustment(valor, mínimo, ,máximo, paso, página, tamaño_de_página) donde los campos son del tipo float. Tenga en cuenta que paso no se usa en este caso, y puede establecerse a 0.
= = =

Paneles

También se pueden insertar paneles... (Gtk.Paned)
...
class miventana(Gtk.ApplicationWindow):

    def __init__(self, app):
        Gtk.Window.__init__(self, title="Paned Example", application=app)
        self.set_default_size(450, 350)

        # widget paned con orientación horizontal
        paned = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL)

        # dos imágenes
        image1 = Gtk.Image()
        image1.set_from_file("gnome.png")
        image2 = Gtk.Image()
        image2.set_from_file("tux.png")

        # añado imágenes y al panel.
        paned.add1(image1)
        paned.add2(image2)
        self.add(paned)
...

52. GTK: Imágenes y etiquetas.

Incrustando una imagen en la ventana

De una primera forma... En la que se crea una imagen desde un fichero como un objeto y se añade a la ventana. GtkImage
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import sys

# Una ApplicationWindow GTK

class miventana(Gtk.ApplicationWindow):
    # Constructor de ApplicationWindow

    def __init__(self, app):
        Gtk.Window.__init__(self, title="Welcome to GNOME", application=app)
        # también puede venir aquí win.show_all()
        self.set_default_size(800,800)
        
        # Crear una imagen
        image = Gtk.Image()
        # Obtenerla desde un fichero
        image.set_from_file("coche.png")
        # añadirla a la ventana.
        self.add(image)

class miaplicacion(Gtk.Application):
    
    # Constructor de Gtk.Application
    def __init__(self):
        Gtk.Application.__init__(self)

    # al activarse la aplicación, crea un objeto miventana.
    # Paso la aplicación "self" como argumento.
    def do_activate(self):
        win = miventana(self)
        # Muestra la ventana
        win.show_all()

    # hacer un startup
    def do_startup(self):
        Gtk.Application.do_startup(self)

# al igual que antes se crea y se ejecuta la aplicación
app = miaplicacion()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
= = =
De otra forma, definiendo la imagen como una clase.
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import sys

# Una Imagen como clase
class miimagen(Gtk.Image):
    
    def __init__(self):
        Gtk.Image.__init__(self)
        self.set_from_file("coche.png")

# Una ApplicationWindow GTK
class miventana(Gtk.ApplicationWindow):
    # Constructor de ApplicationWindow

    def __init__(self,app):
        Gtk.Window.__init__(self, title="Welcome to GNOME", application=app)
        # también puede venir aquí win.show_all()
        self.set_default_size(800,800)

class miaplicacion(Gtk.Application):
    
    # Constructor de Gtk.Application
    def __init__(self):
        Gtk.Application.__init__(self)

    # al activarse la aplicación, crea un objeto miventana.
    # Paso la aplicación "self" como argumento.
    def do_activate(self):
        win = miventana(self)
        # añade la imagen
        win.add(miimagen())
        # Muestra la ventana
        win.show_all()

    # hacer un startup
    def do_startup(self):
        Gtk.Application.do_startup(self)

# al igual que antes se crea y se ejecuta la aplicación
app = miaplicacion()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
= = =

Etiquetas

Según la documentación GTK usa en Python cadenas str codificadas en utf-8. Ver apartado Unicode en GTK+
El siguiente código ilustra las dos formas de llamar a una etiqueta, como una clase o directamente como una instancia de la clase Gtk.Label. Son métodos que no se pueden usar a la vez, por eso uno está comentado.
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import sys

"""
#Poner una etiqueta como clase
class  mietiqueta(Gtk.Label):
    
    def __init__(self):
        Gtk.Label.__init__(self)
        self.set_text("¡Hola, soy una etiqueta con clase")"""

# Una ApplicationWindow GTK
class miventana(Gtk.ApplicationWindow):
    # Constructor de ApplicationWindow

    def __init__(self,app):
        Gtk.Window.__init__(self, title="Welcome to GNOME", application=app)
        # también puede venir aquí win.show_all()
        self.set_default_size(800,800)
        
        # Quitar este ejemplo si se quiere 
        # comprobar como se hace con una clase
        # =======================
        # Otra etiqueta sin clase
        # =======================
        label2 = Gtk.Label()
        label2.set_text("Soy una etiqueta sin clase")
        self.add(label2)
        # =======================

class miaplicacion(Gtk.Application):
    
    # Constructor de Gtk.Application
    def __init__(self):
        Gtk.Application.__init__(self)

    # al activarse la aplicación, crea un objeto miventana.
    # Paso la aplicación "self" como argumento.
    def do_activate(self):
        win = miventana(self)
        # añade la imagen
        """ win.add(mietiqueta()) """
        # Muestra la ventana
        win.show_all()

    # hacer un startup
    def do_startup(self):
        Gtk.Application.do_startup(self)

# al igual que antes se crea y se ejecuta la aplicación
app = miaplicacion()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
= = =
Otros métodos son los siguientes:
  • set_line_wrap(True) rompe líneas si el texto de la etiqueta excede el tamaño del widget.
  • set_justify(Gtk.Justification.LEFT) (o Gtk.Justification.RIGHT, Gtk.Justification.CENTER, Gtk.Justification.FILL) establece la alineación en etiquetas con más de una línea
  • Para decorar el texto se puede usar set_markup("texto"), donde "texto" es un texto en el lenguaje de marcación Pango.
En el siguiente ejemplo se modifica el programa anterior y se cambia el __init__ de miventana. Atención a modificar el tamaño de la misma a 100, 100.
...
    def __init__(self,app):
        Gtk.Window.__init__(self, title="Welcome to GNOME", application=app)
        # también puede venir aquí win.show_all()
        self.set_default_size(100,100)
        
        # Quitar este ejemplo si se quiere 
        # comprobar como se hace con una clase
        # =======================
        # Otra etiqueta sin clase
        # =======================
        label2 = Gtk.Label()
        label2.set_text("Este es un texto muy largo, tan largo que creo que no cabrá en el widget ventana")
        label2.set_line_wrap(True)
        label2.set_justify(Gtk.Justification.CENTER)
        # label2.set_markup("<b>Texto</b> en varios <small>tamaños</small> o <span foreground='darkred' background='peru'>en color</span>")
        self.add(label2)
        # =======================
...

= = = 
 

51. Aplicaciones de usuario: GUIs con GTK en Python: window y applicationwindow

En general, una interfaz es el medio de contacto que un usuario establece con una máquina: una serie de botones, una línea de comandos, o, un entrono gráfico presente en una ventana cuyos elementos son gráficos (botones, etiquetas, imágenes...). A este último se le denomina GUI (graphical user interface)

¿Qué es GTK?

GTK+ (Gimp Toolkit) son una serie de bibliotecas gráficas inicialmente pensadas para la creación de interfaces gráficas de usuario. Dibujan en una ventana un conjunto de widgets (botones, etiquetas, etc.) GTK+ se puede usar en cualquier plataforma; aunque inicialmente se pensó para entornos de escritorio como GNOME (Ubuntu) puede usarse bajo Windows y bajo MAC OS.
Desarrollando software en C u otros lenguajes como Python puede usarse estas bibliotecas. Con GTK+ se pueden desarrollar software bajo varias licencias, libres y propietarias. Cuando llamamos desde un programa a la biblioteca de otra aplicación externa decimos que estamos utilizando una API de esa aplicación. Usaremos pues las APIs de GTK+
  1. Proyecto GTK
  2. GTK+ en Wikipedia.
 

Abriendo una ventana (window)

En esta aplicación llamamos a dos APIs de GTK+. La primera es la clase Aplicación "GtkApplication" y la segunda es la clase Ventana "GtkWindow". Construyendo una interfaz gráfica significa crear una aplicación en una ventana.
En el siguiente ejemplo, abriré una ventana en el S.O. Activo una clase llamada miaplicacion a la cual pertenecerá una ventana.
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import sys

class miaplicacion(Gtk.Application):

    def do_activate(self):
        # Crear una ventana GTK que pertenece a la misma aplicación
        ventana = Gtk.Window(application=self)
        # Pongo un título
        ventana.set_title("¡Bienvenidos a GNOME!")
        # activo o muestro la ventana
        ventana.show_all()

# crea una instancia de la clase miaplicacion
app = miaplicacion()
# ejecuto la misma pasando los argumentos sys.argv
exit_status = app.run(sys.argv)
# Sale de la aplicación leyendo el status de la misma.
sys.exit(exit_status)
Intenta introducir en la ventana las siguientes opciones
  • set_default_size(m,n), siendo m y n en píxeles el tamaño de la ventana. Si m ó n = -1, se toma el tamaño predeterminado.
  • set_position(Gtk.WindowPosition.CENTER)
    • Los modificadores posibles son Gtk.WindowPosition.CENTER, Gtk.WindowPosition.NONE, Gtk.WindowPosition.MOUSE, Gtk.WindowPosition.CENTER_ALWAYS, Gtk.WindowPosition.CENTER_ON_PARENT
Para tener más referencias, consultar las funciones de las APIs correspondientes. GtkApplication y GtkWindow

Abriendo una ventana con menús (applicationwindow)

La ventana de ApplicationWindow es la que puede soportar GMenu. Es decir, es una ventana a la que se puede incorporar una barra de menú. Se utiliza una subclase de GtkWindow llamada GtkApplicationWindow.
# -*- coding: utf-8 -*-
from gi.repository import Gtk
import sys

# Una ApplicationWindow GTK

class miventana(Gtk.ApplicationWindow):
    # Constructor de ApplicationWindow

    def __init__(self, app):
        Gtk.Window.__init__(self, title="Welcome to GNOME", application=app)
        # también puede venir aquí win.show_all()

class miaplicacion(Gtk.Application):
    
    # Constructor de Gtk.Application
    def __init__(self):
        Gtk.Application.__init__(self)

    # al activarse la aplicación, crea un objeto miventana.
    # Paso la aplicación "self" como argumento.
    def do_activate(self):
        win = miventana(self)
        # Muestra la ventana
        win.show_all()

    # hacer un startup
    def do_startup(self):
        Gtk.Application.do_startup(self)

# al igual que antes se crea y se ejecuta la aplicación
app = miaplicacion()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
Explicando el código:
  • Se vuelve a crear una clase miaplicacion, llamando a la API Gtk.Application.
  • Esta aplicación define dos métodos que se activan cuando recibe dos señales distintas (ver señales en Gtk.application).
    • Cuando la aplicación se ejecuta se dispara la señal startup antes de dibujar la ventana. Se utiliza para generar código  antes de tener la interfaz gráfica en sí.
    • Después, y dependiendo de cómo sea la aplicación se disparan las señales open o activate.
  • La clase miventana , llama a la API Gtk.Applicationwindow.
    • Simplemente se construye llamando al constructor de la clase Gtk.Window.
  • Por lo demás, es igual al código del apartado anterior.