En este tutorial se va a escribir una aplicación GTK+ muy sencilla que carga un archivo e imagen y lo muestra. Aprenderá a:
Escribir una interfaz de usuario básica en Python
Trabajar con eventos conectando señales a manejadores de señales
La disposición de las interfaces de usuario usando contenedores
Cargar y mostrar archivos de imagen
Necesitará lo siguiente para poder seguir este tutorial:
Una copia instalada del EID Anjuta
Conocimiento básico del lenguaje de programación Python
Antes de empezar a programar, deberá configurar un proyecto nuevo en Anjuta. Esto creará todos los archivos que necesite para construir y ejecutar el código más adelante. También es útil para mantener todo ordenado.
Inicie Anjuta y pulse
Seleccione
Asegúrese de desactivar
Pulse
Vea como queda una aplicación GTK+ muy básica en Python:
from gi.repository import Gtk, GdkPixbuf, Gdk
import os, sys
class GUI:
def __init__(self):
window = Gtk.Window()
window.set_title ("Hello World")
window.connect_after('destroy', self.destroy)
window.show_all()
def destroy(window, self):
Gtk.main_quit()
def main():
app = GUI()
Gtk.main()
if __name__ == "__main__":
sys.exit(main())
Eche un vistazo a lo que está pasando:
La primera línea importa el espacio de nombres de GTK+ (es decir, incluye la biblioteca GTK+). Las bibliotecas las proporciona «GObject Introspection (gi)», que implementa asociaciones de lenguajes para la mayoría de las bilbiotecas de GNOME.
En el método __init__
de la clase GUI
se crea un Gtk.Window
(vacía), establece su título y después conecta una señal para salir de la aplicación una vez que se cierra la ventana. Es bastante simple, más acerca de señales en adelante.
Después se define destroy
que simplemente sale de la aplicación. Se le llama a través de la señal destroy
conectada anteriormente.
El resto del archivo realiza la inicialización de GTK+ y muestra la IGU.
Este código está listo para usarse, por lo que puede probarlo usando
Las señales son un concepto clave en la programación en GTK. Cuando pasa algo en un objeto, emite una señal; por ejemplo, cuando se pulsa un botón, emite la señal clicked
. Si quiere que su programa haga algo cuando ocurre ese evento, debe conectar una función (un «manejador de la señal») a esa señal. Aquí hay un ejemplo:
def button_clicked () :
print "you clicked me!"
b = new Gtk.Button ("Click me")
b.connect_after ('clicked', button_clicked)
Las dos últimas líneas crean un Gtk.Button
llamado b
y conectan su señal clicked
con la función button_clicked
que se ha definido anteriormente. Cada vez que se pulsa un botón, se ejecuta el código de la función button_clicked
. Esto sólo imprime un mensaje aquí.
Los widgets (controles, como botones y etiquetas) se pueden organizar en la ventana usando contenedores. Puede organizar el diseño mezclando diferentes tipos de contenedores, como cajas y rejillas.
Una Gtk.Window
es en sí misma un tipo de contenedor, pero sólo puede poner un widget directamente en ella. Se quieren poner dos widgets, una imagen y un botón, por lo que se necesita poner un contenedor «de mayor capacidad» dentro de la ventana para que contenga otros widgets. Hay varios tipos de contenedores disponibles, pero aquí se usará una Gtk.Box
. Una Gtk.Box
puede contener varios widgets, organizados horizontal o verticalmente. Se pueden hacer diseños más complejos poniendo varias cajas dentro de otras, y así sucesivamente.
Hay un diseñador de interfaces gráficas llamado
Añada la caja y los widgets a la ventana. Inserte el siguiente código en el método __init__
, justo debajo de la línea window.connect_after
:
box = Gtk.Box()
box.set_spacing (5)
box.set_orientation (Gtk.Orientation.VERTICAL)
window.add (box)
La primera línea crea una Gtk.Box
llamada box
y las siguientes líneas establecen dos de sus propiedades: la orientación (orientation
) se establece a «vertical» (por lo que los widgets se colocan en columna), y el espaciado (spacing
) entre los widgets se establece a 5 píxeles. La siguiente línea añade la Gtk.Box
recién creada a la ventana.
De momento, la ventana sólo contiene una Gtk.Box
vacía, y si ejecuta el programa ahora no verá ningún cambio (la Gtk.Box
es un contenedor transparente, por lo que no puede que está ahí).
Para añadir algunos widgets a la Gtk.Box
, inserte el siguiente código justo debajo de la línea window.add (box)
:
self.image = Gtk.Image()
box.pack_start (self.image, False, False, 0)
La primera línea crea una Gtk.Image
llamada image
, que se usará para mostrar un archivo de imagen. Como se necesitará más tarde en el manejador de la señal, se definirá como variable a nivel de clase. Deberá añadir image = 0
al principio de la clase GUI
. Entonces se añade el widget de imagen (packed) en el contenedor box
usando el método pack_start
de GtkBox.
pack_start
toma 4 argumentos: el widget que añadir a la GtkBox (child
); si la Gtk.Box
debe crecer cuando añada el widget nuevo (expand
); si el widget nuevo debe tomar todo el espacio adicional creado si la Gtk.Box
crece (fill
); y cuánto espacio debe haber, en píxeles, entre el widget y sus vecinos dentro de la Gtk.Box
(padding
).
Los contenedores (y los widgets) de GTK+ se expanden dinámicamente, si les deja, para rellenar el espacio disponible. No posicione widgets indicando unas coordenadas x-y precisas en la ventana; en lugar de eso, se posicionan relativos a otro. Esto hace que el manejo de la redimensión de las ventanas sea más fácil, y que los widgets tengan un tamaño sensible automática en la mayoría de las situaciones.
También tenga en cuenta cómo se organizan los widgets de manera jerárquica. Una vez empaquetados en la Gtk.Box
, la Gtk.Image
se considera un hijo de la Gtk.Box
. Esto le permite tratar a todos los hijos de un widget como un grupo; por ejemplo, puede ocultar la Gtk.Box
, lo que haría que también se ocultaran todos sus hijos a la vez.
Ahora inserte estas dos líneas, justo debajo de las dos que acaba de añadir.
button = Gtk.Button ("Open a picture...")
box.pack_start (button, False, False, 0)
Estas líneas son similares a las dos primeras, pero esta vez crean un Gtk.Button
y lo añaden a la box
. Tenga en cuenta que se está estableciendo el argumento expand
(el segundo) a False
, mientras que para la Gtk.Image
se estableció a True
. Esto hará que la imagen tome todo el espacio disponible y que el botón tome sólo el espacio que necesite. Cuando se maximiza la ventana, el tamaño del botón será el mismo, pero el tamaño de la imagen aumentará junto con el resto de la ventana.
clicked
del botónCuando el usuario pulse sobre el botón
El primer paso es conectar la señal clicked
del botón a una función manejadora de la señal, llamada on_open_clicked
. Ponga este código inmediatamente después de la línea button = Gtk.Button()
en la que se creó el botón:
button.connect_after('clicked', self.on_open_clicked)
Esto conectará la señal clicked
al método on_open_clicked
que se definirá más adelante.
Ahora se puede crear el método on_open_clicked
. Inserte lo siguente dentro del bloque de clase GUI
, después del método __init__
:
def on_open_clicked (self, button):
dialog = Gtk.FileChooserDialog ("Open Image", button.get_toplevel(), Gtk.FileChooserAction.OPEN);
dialog.add_button (Gtk.STOCK_CANCEL, 0)
dialog.add_button (Gtk.STOCK_OK, 1)
dialog.set_default_response(1)
filefilter = Gtk.FileFilter ()
filefilter.add_pixbuf_formats ()
dialog.set_filter(filefilter)
if dialog.run() == 1:
self.image.set_from_file(dialog.get_filename())
dialog.destroy()
Esto es un poco más complicado que todo lo que se ha intentado hasta ahora, así que se puede desglosar:
La línea que empieza por dialog
crea un diálogo SAVE
si se quisiese guardar un archivo; y transient_for
, que establecer la ventana padre del diálogo.
Las dos siguientes líneas añaden los botones add_button
es el valor (entero) que se devuelve cuando se pulsa el botón: 0 para
Note que se está usando nombres de botones del almacén de GTK, en lugar de escribir manualmente «Cancelar» o «Abrir». La ventaja de usar nombres del almacén es que las etiquetas de los botones ya estarán traducidas en el idioma del usuario.
set_default_response
determina qué botón se activará si el usuario hace una doble pulsación o presiona
Las tres líneas siguientes restringen el diálogo Gtk.Image
. Primero se crea un objeto de filtro; luego se añaden los tipos de archivos soportados por el Gdk.Pixbuf
(que incluye la mayoría de los formatos de imagen como PNG y JPEG) al filtro. Por último, se establece que este filtro sea el filtro del diálogo
dialog.run
muestra el diálogo dialog.run
devolverá el valor (devolvería si el usuario pulsara if
comprueba esto.
Asumiendo que el usuario pulsó file
de la Gtk.Image
al nombre del archivo de imagen seleccionada por el usuario. La Gtk.Image
cargará y mostrará la imagen elegida.
En la última línea de este método se destruye el diálogo
Todo el código que necesita debe estar en su lugar, así que trate de ejecutar el código. Esto debería ser todo; un visor de imágenes completamente funcional (y un completo tour sobre Python y GTK+) en poco tiempo.
Si tiene problemas con este tutorial, compare su código con este código de referencia.
Aquí hay algunas ideas sobre cómo puede extender esta sencilla demostración:
Haga que el usuario selecciona una carpeta en vez de un archivo, y proporcione controles para moverse por todas las imágenes de una carpeta.
Aplicar filtros aleatorios y efectos a la imagen cuando se carga y permitir al usuario guardar la imagen modificada.
GEGL proporciona la capacidad de manipular imágenes de manera potente.
Permitir al usuario cargar imágenes desde recursos de red compartidos, escáneres y otras fuentes más complicadas.
Puede usar GIO para gestionar transferencias de archivos de red y similares, y GNOME Scan para gestionar el escaneado.