Tutorial de Glade

01/11/2008

Tutorial de Glade

Qué?

Este tutorial intenta proporcionar una guía paso a paso para los desarrolladores de Haskell que quieren escribir aplicaciones GTK+ usando Glade. Asumimos que estás usando Linux aunque tanto el conjunto de herramientas Gtk+, el diseñador de interfaces Glade y Gtk2Hs están disponibles en otras plataformas.

Esta página tutorial es una adaptación para Haskell y Gtk2Hs de un tutorial original para C y la GTK+ C API

Necesitarás:

Crearás una aplicación tipo "Hello World" como esta:

Por qué usar Glade?

Glade permite al desarrollador un diseño rápido y eficiente de una aplicación visual y entonces concentrarse en la implantación del programa en vez de preocuparse de los problemas del interfaz de usuario.

¿Cómo?

Inicia Glade. Se abrirá con un nuevo proyecto sin título. Entonces verás lo siguiente. (Pulsa en las imágenes para verlas a tamaño completo) :

La paleta en la izquierda permite añadir widgets a la aplicación, mientras que el panel de propiedades, abajo a la derecha, permite seleccionar las propiedades del widget seleccionado para ser modificado y las señales (lo veremos después) que serán definidas

La primera cosa que vamos a necesitar es un lugar para añadir los widgets, así que vamos a crear una ventana, pulsando en el icono Ventana de la paleta.

icono ventana

Un widget tipo ventana aparecerá en el canvas (espacio de trabajo) preparado para ser modificado. Fíjate en las propiedades de la ventana (bajo la pestaña General) en particular que su denominación es window1 Vamos a asignarle un nombre más descriptivo para ser mostrado en la barra de título de la ventana en tiempo de ejecución. Escribe Hola Gtk2Hs el el campo Título.

Perfecto, la ventana está ya preparada, así que vamos a añadir algunos botones y etiquetas... pero espera! Todavía no podemos añadir widgets. Los widgets en GTK+ están empaquetados. Empaquetar widgets puede parecer pesado al principio pero realmente es un método ingenioso para permitir muchas cosas interesantes, la principal de ellas es el resizing (cambio de tamaño de la ventana) automático. Cuando un usuario cambia el tamaño de una aplicación, normalmente queremos que los widgets de la ventana cambien también su tamaño para aprovechar mejor el nuevo espacio, ya sea expandiendose o comprimiendose. El empaquetado permite que esto se haga automáticamente y afortunadamente libera al programador de escribir código de adaptación al nuevo tamaño. El empaquetado se realiza creando cajas o tablas. Estos widgets son invisibles, lo que quiere decir que no se ven en el momento de la ejecución del programa, sin embargo tienen efecto en la aplicación.

Si volvemos a la aplicación que estamos creando, puedes ver que se puede dividir la ventana en tres filas: la etiqueta de arriba, el campo de entrada de texto en el medio y los dos botones de abajo. Así que lo que vamos a hacer es crear un widget VBox (Caja vertical) con tres filas. Pulsa el icono Caja Vertical en la paleta y después pulsa en cualquier lugar de la ventana que acabamos de crear.

icono Caja Vertical

Indica que quieres tres filas y pulsa Aceptar. La ventana queda ahora dividida en tres filas, preparada para añadir más widgets. Añade un widget de etiqueta a la primera fila.(superior)

icono de etiqueta(puede ser diferente a este)

En la segunda fila coloca un widget de entrada de texto.

icono de entrada de texto

La ventana de la aplicación y su correspondiente árbol de widgets debería parecerse a esto:

Antes de poder añadir los botones en la tercera fila, debemos crear una caja horizontal dentro de ella. El nuevo widget caja horizontal debe tener dos columnas, una para cada botón. Esto lo conseguimos pulsando el icono Caja Horizontal de la paleta y pulsando la tercera fila de la ventana.

icono caja horizontal

Indica dos columnas y pulsa Aceptar. La tercera fila se divide ahora horizontalmente en dos secciones iguales.

Quizá sea un buen momento para salvar el proyecto. Por defecto, Glade lo salva como un fichero .glade en tu directorio raíz. Nombra el proyecto como holagtk2hs.

amos a hacer los botones. Cada una de las secciones que creamos en la tercera fila de la caja vertical alojará un botón, así que añadamos un widget de botón pulsando el icono botón en la paleta y después pulsando en la primera (izquierda) sección. Haz lo mismo para la segunda sección.

icono de botón

Perfecto, así que nuestros botones están en su lugar:

Ahora vamos a cambiar algunas propiedades de empaquetado para mejorar el aspecto y para darle identidad a nuestros botones (recuerda que puedes seleccionar facilmente los widgets desde el panel Inspector) Lo seleccionas en el árbol y modificas las propiedades con el inspector.

Lo mejor de desarrollar con Glade es que ves inmediatamente el aspecto que tendrá tu aplicación, así que modifica las propiedades hasta que consigas el aspecto que quieres.

La última propiedad que vamos a cambiar antes de comenzar con el código. Cambia la propiedad Etiqueta en la pestaña General para label1 a Dime tu nombre: y salva el proyecto

Si miras ahora en tu directorio principal (o el directorio que hayas elegido para guardar el proyecto) todo lo que podrás ver es el fichero con la extensión .glade. Es un fichero XML y puedes inspeccionarlo con un editor o un navegador. Si lo haces, verás cada widget que has especificado como un elemento XML, cada uno con un atributo id. Este nombre único, en nuestro caso asignado por defecto por Glade es el que usará Gtk2Hs para acceder a los widgets (como puedes ver en //Listado de código A// más abajo).

Nota: En los tiempos oscuros y nebulosos, era una práctica recomendada para los programadores de C, generar código C a partir de sus diseños de intarfaz de Glade. Glade version 2 proporciona este servicio mediante el boton Construir en la barra de herramientas. Sin embargo deberías ignorar esta opción ya que sólo se aplica al lenguaje C. De hecho la recomendación actual -incluyendo a los programadores de C- es cargar el interfaz gráfico en tiempo de ejecución, lo que resulta mucho más mantenible y flexible.

Sin escribir una sola línea de código hemos creado una estructura completa de Interfaz con el usuario. Ahora hace falta un poco de código para que nuestra aplicación se ejecute.

En primer lugar, algo de administración. Importar los módulos Gtk2Hs necesarios en las líneas 3 y 4, inicializar los gráficos en la línea 7, mostrar la ventana en la línea 11 una vez que se han añadido todos los widgets, y llamar a la función gráfica principal al final. Esta es la plantilla básica para todos los módulos de usuario que usen Gtk2Hs Para utilizar tus ficheros .glade debes añadir algo como lo que aparece en la línea 8. Si se encuentra el fichero especificado, tienes un modo de acceder a todos los widgets desde el programa.

  1  module Main where
  2
  3  import Graphics.UI.Gtk
  4  import Graphics.UI.Gtk.Glade
  5
  6  main = do
  7      initGUI
  8      Just xml <- xmlNew "hellogtk2hs.glade"
  9      window   <- xmlGetWidget xml castToWindow "window1"
  10     onDestroy window mainQuit
  11     widgetShowAll window
  12     mainGUI

Listado de código A

La línea 9 muestra como conseguir el acceso a un widget específico usando la función general xmlGetWidget. El primer argumento fija al tipo de fuente de información, el segundo indica el tipo de widget y el tercero es el nombre del widget específico que queremos manipular (su atributo id) tal y como se ha especificado en el XML. Usaremos esta función continuamente. Finalmente, la línea 10 contiene la acción que debe realizarse cuando el usuario cierra la ventana principal.

Si tienes instalado el compilador de Haskell de Glasgow (ghc-Glasgow Haskell Compiler) puedes usar su versión interactiva, ghci para probar rápidamente qué tenemos hasta el momento con nuestra función main. (Si no te funciona el intérprete prueba con el compilador ya que hay alguna incompatibilidad de funciones)

Todo esto es divertido y bueno, y bonito, pero nuestra aplicación es todavía palabrería ya que todavía no hace nada, así que empecemos a codificar! </p>

Ya que esto no es más que un tutorial introductorio, no vamos a hacer nada demasiado especial. Lo que queremos que ocurra es que el usuario escriba su nombre en la entrada de texto y después -cuando pulse Aplicar- cambiar el texto de la etiqueta para que lo salude con un mensaje en el que escriba, Hola seguido por el nombre que haya introducido. Además el botón Cerrar, debe cerrar la aplicación.

La idea es que cuando le sucede a un widget algo que nos interese (por ejemplo que se pulse un botón), se emite una señal, y el programador codifica lo que se debe ejecutar como respuesta a ese hecho. Por ejemplo:

  onClicked button $ do
    ...

(Recuerda que el operador $ es el operador de aplicación de funciones asociativas por la derecha y sólo se usa aquí para no tener que colocar todo el bloque do entre paréntesis.)

Empecemos creando dos manejadores de señal, uno para cada botón. Primero el botón Close que es el más sencillo de implementar. Añadimos un poco de código trivial, únicamente para ver nuestro evento en acción. La función putStrLn escribe en la salida estándar, así que vamos a usarla para saber que el botón button2 ha sido pulsado

Antes de mainGUI,que siempre debe ser la última instrucción, y widgetShowAll window, añade las dos líneas siguientes:

  closeButton <- xmlGetWidget xml castToButton "button2"
  onClicked closeButton $ do
      putStrLn "Close Button Clicked"

Como puedes ver, estás usando la función xmlGetWidget de nuevo, pero con un tipo diferente de widget ahora. Para conseguir que el botón Cerrar funcione como su nombre indica deberías usar la función mainQuit en vez de putStrLn. Si haces esto en ghci verás que pulsando el botón volverá a ghci, pero dejará la ventana y sus descendientes en la pantalla. Cuando intentes cerrar la ventana, probablemente recibirás un mensaje del sistema operativo indicando que la aplicación no responde o algo similar. Así que lo que necesitas es:

  onClicked closeButton $ do
      widgetDestroy window

Esto ya si funciona, y gracias a la línea 10 en el Listado de Código A de arriba, el programa saldrá limpiamente. Y tendrás un botón Cerrar completamente funcional! </p>

Recapitulemos y recordemos lo que queremos que haga el programa cuando pulsemos el botón Aplicar. Después de que el usuario escriba su nombre en el campo de texto, queremos que en la etiqueta de arriba cambie el mensaje y ahora ponga "Hola usuario", donde usuario representa el texto que el usuario introdujo. Así este proceso precisa de dos acciones: recuperar información de un widget y establecer una propiedad de un widget..

Las funciones específicas de los widgets están bien documentadas. Incluso hay un buscador de la API. Entre otras cosas, podemos encontrar las funciones específicas para nuestras necesidades inmediatas: etiquetas (labels) y campos de texto (text entry fields).

Lo primero que necesitamos es un modo de obtener el texto introducido por el usuario. Esto lo podemos hacer con la función get y el atributo entryText. Después necesitamos una manera de establecer el texto en nuestra etiqueta. Esto se hace con la función set y el atributo labelText. Y, por supuesto necesitamos acceder a los nuevos widgets que queremos usar.Las líneas 14-16 en Listado de código B no te sorprenderán, y la funcionalidad de las líneas 17-19 tampoco es ingeniería aeroespacial!

  1  module Main where
  2
  3  import Graphics.UI.Gtk
  4  import Graphics.UI.Gtk.Glade
  5
  6  main = do
  7      initGUI
  8      Just xml    <- xmlNew "hellogtk2hs.glade"
  9      window      <- xmlGetWidget xml castToWindow "window1"
  10     onDestroy window mainQuit
  11     closeButton <- xmlGetWidget xml castToButton "button2"
  12     onClicked closeButton $ do
  13         widgetDestroy window
  14     label       <- xmlGetWidget xml castToLabel "label1"
  15     entry       <- xmlGetWidget xml castToEntry "entry1"
  16     applyButton <- xmlGetWidget xml castToButton "button1"
  17     onClicked applyButton $ do
  18         name <- get entry entryText
  19         set label [ labelText := "Hello " ++ name ]
  20     widgetShowAll window
  21     mainGUI

Listado de código B

Hecho! Guarda el programa como holagtk2hs.hs en el mismo directorio que nuestro fichero Glade XML, compilalo usando ghc, y ejecuta el nuevo binario:

  $ ghc --make HelloGtk2Hs.hs -o hellogtk2hs
  $ ./hellogtk2hs

Escribe tu nombre en el campo de entrada de texto y pulsa Aplicar. Hola Usuario! no muy espectacular pero...

Ejercicio:

Busca en la documentación de la API de Gtk2Hs y localiza la función del evento que se genera cuando el usuario pulsa la tecla Enter y úsalo para implementar al misma funcionalidad que proporciona el botón aplicar

Ejercicio:

Ejecuta uno de los programas con ghci, usando el comando main, y cierra la ventana, pero sin salir del módulo. Ahora abre el proyecto en Glade. Cambia el nombre de la ventana y pulsa Guardar para que se actualice el fichero .glade. Ahora ejecuta ./holagtk2hs de nuevo (o main en ghci) y comprueba que el nombre ha cambiado Experimenta para encontrar otros cambios que puedas hacer en el GUI manteniendo el mismo código Haskell.

partir de aquí:

Para Gtk2Hs mira Una Introducción a Gtk2Hs, Una librería gráfica de Haskell por Kenneth Hoste.(en inglés)

El documento GNOME Human Interface Guidelines (HIG) (Guía para desarrollar el interfaz con GNOME) contiene consejos detallados sobre como hacer un interfaz de usuario con buen aspecto y que encaje con el estilo del escritorio de GNOME. Muchos de los consejos se aplican a cualquier interfaz de usuario y no sólo al escritorio de GNOME.

Créditos e información de Copyright

La versión original C de este tutorial fue escrita por Eddy Ahmed. Fue adaptada para Haskell y Gtk2Hs por Hans van Thiel, en Octubre de 2006. El texto y las imágenes fueron actualizadas a Glade 3 por Alex Tarkovsky en Agosto de 2007.

Copyright 2002 Eddy Ahmed.

El texto y las imágenes han sido adaptadas y/o usadas con permiso del autor y propietario del copyright.