Visor de imágenes (Vala) Algo más que una sencilla aplicación «Hola mundo» en GTK+. Proyecto de documentación de GNOME gnome-doc-list@gnome.org Johannes Schmid jhs@gnome.org Philip Chimento philip.chimento@gmail.com Tiffany Antopolski tiffany.antopolski@gmail.com Marta Maria Casetti mmcasetti@gmail.com 2013 Daniel Mustieles daniel.mustieles@gmail.com 2011 - 2017 Nicolás Satragno nsatragno@gmail.com 2012 - 2013 Jorge González jorgegonz@svn.gnome.org 2011 Visor de imágenes

En este tutorial se va a crear un programa que abre un archivo de imagen y lo muestra. Aprenderá a:

Cómo configurar un proyecto básico usando el EID Anjuta.

Cómo escribir una aplicación de GTK+ en Vala

Algunos conceptos básicos de programación en GObject

Necesitará lo siguiente para poder seguir este tutorial:

Conocimiento básico del lenguaje de programación Vala.

Una copia instalada de Anjuta.

Puede que la referencia de la API de gtk+-3.0 le resulte útil, aunque no es necesaria para seguir el tutorial.

Crear un proyecto en Anjuta

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 Crear un proyecto nuevo o ArchivoNuevoProyecto para abrir el asistente de proyectos.

En la pestaña Vala seleccione GTK+ (simple), pulse Continuar, y rellene los detalles en las siguientes páginas. Use visor-imagenes como nombre del proyecto y de la carpeta.

Asegúrese de que Usar GtkBuilder para la interfaz del usuario está desactivado, ya que, en este tutorial, la IU se creará manualmente.

Aprenderá a usar el constructor de interfaces en el tutorial del afinador de guitarra.

Pulse Continuar y luego Aplicar y se creará el proyecto. Abra src/visor-imagenes.vala desde las pestañas Proyecto o Archivo. Verá el siguiente código:

using GLib; using Gtk; public class Main : Object { public Main () { Window window = new Window(); window.set_title ("Hello World"); window.show_all(); window.destroy.connect(on_destroy); } public void on_destroy (Widget window) { Gtk.main_quit(); } static int main (string[] args) { Gtk.init (ref args); var app = new Main (); Gtk.main (); return 0; } }
Construir el código por primera vez

El código carga una ventana (vacía) desde el archivo de descripción de interfaz de usuario y la muestra. Se ofrecen más detalles a continuación; omita esta lista si entiende los conceptos básicos:

Las dos líneas using importan espacios de nombres, por lo que no hay que nombrarlas explícitamente.

El constructor de la clase Main crea una ventana (vacía) nueva y conecta una señal para salir de la aplicación cuando se cierra esa ventana.

Conectar señales es como se define lo que pasa cuando pulsa un botón, o cuando ocurre algún otro evento. Aquí, se llama a la función destroy (y se sale de la aplicación) cuando cierra la ventana.

La función static main se ejecuta de manera predeterminada cuando inicia una aplicación en Vala. Llama a algunas funciones que crean la clase Main y configuran y ejecutan la aplicación. La función Gtk.main inicia el bucle principal de GTK+, que ejecuta la interfaz de usuario y empieza a escuchar eventos (como pulsaciones del ratón y del teclado).

Este código está listo para usarse, por lo que puede compilarlo pulsando ConstruirConstruir proyecto (o pulsando MayúsF7).

Cambie la Configuración a Predeterminada y pulse Ejecutar para configurar la carpeta de construcción. Sólo necesita hacer esto una vez, para la primera construcción.

Crear la interfaz de usuario

Ahora se dará vida a la ventana vacía. GTK+ organiza la interfaz de usuario con varios Gtk.Container que pueden contener otros widgets e incluso otros contenedores. Aquí se usará el contenedor más sencillo disponible, una Gtk.Box.

Añada las siguientes líneas a la parte superior de la clase Main:

private Window window; private Image image;

Ahora reemplace el constructor actual con el siguiente:

public Main () { window = new Window (); window.set_title ("Image Viewer in Vala"); // Set up the UI var box = new Box (Orientation.VERTICAL, 5); var button = new Button.with_label ("Open image"); image = new Image (); box.pack_start (image, true, true, 0); box.pack_start (button, false, false, 0); window.add (box); // Show open dialog when opening a file button.clicked.connect (on_open_image); window.show_all (); window.destroy.connect (main_quit); }

Las dos primeras líneas son partes de la IGU a las que se debe acceder desde más de un método. Se declaran aquí, por lo que son accesibles mediante la clase en vez de serlo solamente en el método en el que se crearon.

Las primeras líneas del constructor crean la ventana vacía. Las siguientes líneas crean los widgets que se quieren usar: un botón para abrir una imagen, el widget de la vista de la imagen en sí y la caja que se usará como contenedor.

Las llamadas a pack_start añaden los dos widgets a la caja y definen su comportamiento. La imagen se expandirá en cualquier espacio disponible, mientras que el botón será tan grande como se necesite. Se dará cuenta de que no se establecen tamaños explícitos de los widgets. Generalmente, en GTK+ no se necesita ya que hace que sea mucho más sencillo tener una distribución que se ve bien con diferentes tamaños de la ventana. A continuación, se añade la caja a la ventana.

Se debe definir qué pasa cuando el usuario pulsa en el botón. GTK+ usa el concepto de señales.

Cuando se pulsa el botón se emite la señal clicked, que se puede conectar a alguna acción (definida en un método de un retorno de la llamada).

Esto se hace usando el método connect de la señal clicked de los botones, que en este caso indica a GTK+ que llame al método del retorno de la llamada on_image_open (todavía no definida) cuando se pulsa el botón. El retorno de la llamada se definirá en la siguiente sección.

En el retorno de la llamada, se debe acceder a los widgets window y image que es por lo que se definen como miembros privados en la parte superior de la clase.

La última llamada a connect asegura que la aplicación termina cuando se cierra la ventana. El código generado por Anjuta llamaba a un método on_destroy que llamaba a Gtk.main_quit, pero simplemente conectando la señal main_quit directamente es más fácil. Puede eliminar el método on_destroy.

Mostrar la imagen

Ahora se definirá el manejador de la señal clicked para el botón mencionado anteriormente. Añada este código después del constructor.

public void on_open_image (Button self) { var filter = new FileFilter (); var dialog = new FileChooserDialog ("Open image", window, FileChooserAction.OPEN, Stock.OK, ResponseType.ACCEPT, Stock.CANCEL, ResponseType.CANCEL); filter.add_pixbuf_formats (); dialog.add_filter (filter); switch (dialog.run ()) { case ResponseType.ACCEPT: var filename = dialog.get_filename (); image.set_from_file (filename); break; default: break; } dialog.destroy (); }

Esto es un poco más complicado, así que se puede desglosar:

Un manejador de señal es un tipo de método de retorno de la llamada al que se llama cuando se emite la señal. Aquí se usan los términos intercambiados.

El primer argumento del método del retorno de la llamada es siempre el widget que envía la señal. A veces hay otros argumentos relativos a la señal que vienen después, pero clicked no tiene ninguno.

En este caso, el button envía la señal clicked, que se conecta al método del retorno de la llamada on_open_image:

button.clicked.connect (on_open_image);

El método on_open_image toma como argumento el botón que ha emitido la señal:

public void on_open_image (Button self)

La siguiente línea interesante es en la que se crea el diálogo para elegir el archivo. El constructor de FileChooserDialog toma el título del diálogo, la ventana padre del diálogo y varias opciones como el número de botones y sus valores correspondientes.

Tenga en cuenta 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.

Las dos líneas siguientes restringen el diálogo Abrir para que sólo muestre archivos que se puedan abrir con GtkImage. GtkImage es un widget que muestra una imagen. Primero se crea un objeto; 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 Abrir.

dialog.run muestra el diálogo Abrir. El diálogo esperará a que el usuario seleccione una imagen; cuando lo haga, dialog.run devolverá el valor ResponseType.ACCEPT de ResponseType (devolvería ResponseType.CANCEL si el usuario pulsara Cancel). La sentencia switch comprueba esto.

Asumiendo que el usuario pulsó Abrir, las siguientes líneas obtienen el nombre de archivo de la imagen seleccionada por el usuario e indican al widget GtkImage que cargue y muestre la imagen elegida.

En la última línea de este método se destruye el diálogo Abrir porque ya no se necesita.

Al destruir se oculta el diálogo automáticamente.

Construir y ejecutar la aplicación

Todo el código debería estar listo para ejecutarse. Pulse ConstruirConstruir proyecto para construir todo otra vez y pulse EjecutarEjecutar para iniciar la aplicación.

Si todavía no lo ha hecho, elija la aplicación src/visor-imagenes en el diálogo que aparece. Finalmente, pulse Ejecutar y disfrute.

Implementación de referencia

Si tiene problemas con este tutorial, compare su código con este código de referencia.

Siguientes pasos

Aquí hay algunas ideas sobre cómo puede extender esta sencilla demostración:

Configúrela de tal manera que la ventana se abra con un tamaño determinado. Por ejemplo, 200x200 píxeles.

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.