Ejemplo de desarrollo de una aplicacion en GTK#

Roberto Pérez

Mono-Hispano

Table of Contents
1. Introduccion
2. Requerimientos para el desarrollo
3. Como compilar y ejecutar las pruebas
4. Como empezar con nuestra aplicacion
5. Creaccion de una ventana simple
5.1. Añadiendo un callback a la ventana
6. Creando nuestros primeros objetos
6.1. Creando una caja de texto
6.2. Creando un boton
6.3. Creando una etiqueta de texto
7. Colocando los widgets en la aplicacion
7.1. Creando nuestro layout de tipo table
7.2. Creando layouts de tipo boxes
7.2.1. Horizontal Boxes ( HBox )
7.2.2. Vertical Box ( VBox )
8. Conectando los eventos
9. Introduciendo una sencilla barra de menu
9.1. Los principales objetos que entran en juego
9.2. Creando la barra de menu
9.3. Finalizando
10. Conclusiones

1. Introduccion

Este tutorial es una pequeña muestra de lo que podemos hacer con gtk# y todo ello con muy poco esfuerzo. No pretende ser un estudio a fondo de la api ya que aun se encuentra en estado inicial, aunque totalmente operativo. Para mas detalles ver la pagina de gtk# y ver la api de gtk+ en www.gtk.org ya que es bastante similar pero con la facilidad que supone la programacion orientada a objetos.

Junto al tutorial se distribuye una pequeña aplicacion. Esta aplicacion es un conversor de medidas entre los sistemas Metrico y Estado Unidense, asi pues podemos convertir libras a kilogramos o grados celsius a farenheit. La aplicacion no es un modelo de programacion orienteda a objetos su objetivo es simplemente ver de una forma didactica como construir una interfaz gráfica de forma sencilla. Asi pues la mejor forma de seguir el tutorial es haciendose un ejemplo sencillo y viendo el código de la aplicación

Sin mas vamos a por el tutorial :)


2. Requerimientos para el desarrollo

Para poder compilar y ejecutar necesitamos el compilador de mono y las librerias de gtk-sharp, que implica tener las librerias gtk2.0 y sus derivadas como glib2.0, etc. Para poder instalarlo, en debian, lo mas facil sin tener que acudir al CVS es instalarse los paquetes que residen en el sitio de alp toker para ello debemos añadir a nuestro sources.list

			deb http://www.atoker.com/mono unstable main
		

Y despues

			# apt-get update
			# apt-get install mono gtk-sharp
		

Una vez realizado esto ya tendremos mono y gtk-sharp en nuestro sistema asi que ya podemos empezar a desarrolar :)


3. Como compilar y ejecutar las pruebas

Una vez que hemos escrito nuestro codigo fuente lo que queremos es compilarlo y probarlo asi que para compilar nuestro codigo tendremos que hacer

			# mcs -r gtk-sharp -r glib-sharp prueba.cs
		

Siendo prueba.cs el archivo con el codigo fuente, los flags -r son para indicar al compilador que use las librerias en la compilacion y linkado del programa.

Una vez tenemos el ejecutable nos lanzamos a ejecutarlo sin mas y el proceso es simple, podemos usar el programa mono a lo mas puro estilo java o bien podemos darle permisos de ejecucion al fichero .exe creado y ejecutarlo como otro ejecutable mas, ya que para eso un modulo del kernel que se instala a la vez que instalamos mono se encargara de que esto sea factible, por tanto usando mono pondremos

			mono prueba.exe
		

Y ejecutando directamente el fichero

			chmod u+x prueba.exe 
			./prueba.exe
		

Esto es, primero le damos permisos de ejecucion y ejecutamos sin mas


4. Como empezar con nuestra aplicacion

Primero empezaremos incluyendo las librerias que vamos a usar, en c# usamos using, que buscando su semejante en otros lenguajes podria ser el #include de C o el import de Java.

Por tanto nuestras primeras lineas serian

		using Gtk;
		using Gdk;
		using GtkSharp;
		using System;
		

Ahora que ya tenemos las librerias "importadas" vamos a definir la clase principal y el punto de entrada al programa esto lo hacemos con:

		public class prueba{
		        public static int Main(string[] args)
			        {
		

Con esto definimos la clase principal llamada prueba, que a diferencia de Java no tiene por que llamarse como el archivo y tambien definimos la funcion principal que puede ser void o int eso si debe ser static para que se puede llamar desde el motor de ejecucion. Ojo que la m de Main es mayuscula y el lenguaje es case-sensitive :)


5. Creaccion de una ventana simple

Lo primero que debemos hacer es crear una ventana donde vamos a poner nuestros objetos. Crear una ventana es muy sencillo, ya lo era usando gtk bajo C y aqui en C# todo es mas sencillo. Por tanto para crear la ventana hacemos

		Gtk.Window win = new Gtk.Window(Gtk.WindowType.Toplevel);
		win.Title = "Conversor de medidas";
		win.SetDefaultSize(300,300);
		

Con la primera linea cremos el objeto ventana, la ventana sera del tipo Toplevel ( esto permanece invariable de gtk+ ), con la segunda linea establecemos el atributo Title a la cadena "Conversor de medidas", este atributo es el que luego se mostrara en la ventana que creemos y finalmente en la tercera linea establecemos su tamaño por defecto.

Realmente solo la primera linea es necesaria, pero es una pequeña muestra de lo que podemos hacer con la ventana y la facilidad con que lo hacemos


5.1. Añadiendo un callback a la ventana

Una vez que tenemos la ventana, tenemos que controlar que una vez que pulsemos el boton cerrar la ventana se cierre limpiamente y no tengamos que finalizar el programa matando al proceso para ello añadimos un callback a la ventana. Lo que en gtk+ haciamos con un signal_connect aqui lo hacemos con EventHandlers, la linea de codigo seria

		win.DeleteEvent += new EventHandler (Window_Delete);
		

Con esta linea lo que decimos es que asocie el evento DeleteEvent al controlador de eventos creado para la ocasion. Creamos el controlador de Eventos mediante el constructor al que le pasamos el nombre de una funcion de nuestro codigo que se va a encargar de gestionar el evento, en este caso Window_Delete.

El uso del operador += da a entender que podemos tener mas de un EventHandler asociado a un evento de un objeto y por tanto podemos "sumarlo" a los ya existentes

El codigo de la funcion Window_Delete seria:

		static void Window_Delete(object obj, EventArgs args)
	        {
			SignalArgs sa = (SignalArgs) args;
		        Application.Quit();
		        sa.RetVal = true;
		}	
		

Con esta funcion que maneja el evento Delete de la ventana, es decir cuando la cerramos lo que estamos diciendo es que cierre la aplicacion y que el valor de retorno de esta sea true, indicando que todo ha sido correcto. Los parametros de entrada de la funcion deben ser esos ya que cuando se la llama internamente desde la aplicacion se le pasa estos datos a la funcion


6. Creando nuestros primeros objetos

En este capitulo vamos a aprender a crear objetos simples que luego añadiremos a nuestra aplicacion para crear la interfaz. Se pueden crear muchos widgets pero aqui vamos a mostrar como crear alguno de ellos ya que la orientacion a objetos que tiene gtk# lo hace muy sencillo de entender y viendo como creamos una caja de texto seguro que sabemos como crear otros widgets simplemente mirando la API.


6.1. Creando una caja de texto

El objeto que representa una caja de texto recibe el mismo nombre que en gtk+, esto es, entry por tanto crear una caja de texto sera tan sencillo como

		Entry caja_de_texto = new Entry();
		

Con esta instruccion declaramos el objeto y lo construimos gracias a la llamada a new Entry(). Una vez que tenemos el objeto podemos acceder y cambiar sus propiedades como el texto que contiene, su anchura o si se puede editar o no. Para establecer las propiedades nada mas facil que referenciarlas desde el objeto y darles un valor, por tanto

		caja_de_texto.Text = "Hola mundo";
		caja_de_texto.Editable = false;
		caja_de_texto.MaxLenght = 100;
		Console.WriteLine("{0}", caja_de_texto.Text);
		

Como se ve en estas lineas cambiamos varias propiedades del objeto caja de texto y obtenemos su valor para sacarlo por pantalla


6.2. Creando un boton

Al igual que hemos creado la caja de texto, ahora vamos a crear un boton, el proceso es exactamente el mismo, solo que cambiando de objeto; por tanto tendremos

		Button boton = new Button();
		Button boton2 = new Button("hola soy un boton");
		

Hemos puesto dos ejemplos ya que hay dos posibilidades de construir el boton, una de ellas es pasandole la etiqueta o bien sin pasarsela. Si no le pasamos la etiqueta que tiene el boton, o bien si queremos cambiarla existe la propiedad 'Label' que contiene el texto que se muestra sobre el boton. Asi por tanto dando un vistazo a sus propiedades mas importantes tenemos

		boton.Click();
		boton.Label = "hola soy un boton";
		boton.Release();
		

Como he dicho antes Label es la propiedad que contiene el texto que se muestra sobre el boton, y tanto click como release son metodos del objeto que hacen que genere los eventos respectivos. En lo referente a los eventos lo trataremos en un capitulo aparte asi que de momento solo vamos a crear los objetos


6.3. Creando una etiqueta de texto

Una vez mas la creacion es muy sencilla y para crear la etiqueta de texto usamos

		Label mi_etiqueta = new Label("Hola esto es una etiqueta");
		

La principal propiedad es Text que nos dice el texto que tiene, al igual que podemos cambiarlo


7. Colocando los widgets en la aplicacion

Muy bien ahora tenemos todos los objetos que queramos, el siguiente paso es colocarlos en nuestra aplicacion. No voy a entrar muy a fondo en todos los layouts que tenemos en gtk#, ya que son los mismos que en gtk+ y por tanto para comprenderlos en su totalidad es mejor mirar la documentacion de gtk+, en este capitulo comentaremos los layouts de tipo table y de tipo vbox y hbox


7.1. Creando nuestro layout de tipo table

Este widget nos creara una disposicion semejante a una tabla para colocar nuestros objetos dentro de el, por tanto el primer paso es crear el widget en cuestion

		Table mi_tabla = new Table(6,3,false);
		

Con esto creamos una tabla de 6 filas y 3 columnas, el ultimo parametro indica si las celdas van a ser homogeneas, en este caso lo establecemos a false. Una vez que hemos creado el objeto vamos a introducir nuestros botones y cajas de texto dentro de el; para ello vamos a usar el metodo 'Attach' del objeto, su uso es el siguiente

		mi_tabla.Attach(objeto, izq, der, superior, inferior,
					opciones_en_x , opciones_en_y, 
					padding_en_x, padding_en_y);
		

Pasamos a explicar brevemente que es cada parametro. El objeto es sin duda el objeto que queremos insertar dentro de la tabla. Los cuatro parametros izq, der, superior e inferior nos van a decir donde encuadramos el objeto, cabe la posibilidad de plantearse por que usar 4 parametros y no solo dos, la respuesta es sencilla ya que podemos tener objetos q ocupen dos celdas juntas, tres o siemplemente una sola, asi que por tanto son necesario los 4 parametros. Por tanto si queremos colocar un objeto en la posicion 1,2 de la tabla los 4 parametros serian 1,2,2,3. Si diera el caso que nuestro objeto tendria que extenderse dentro de la fila 2 por las columnas 3,4 y 5 los cuatro numeros serian 3,6,1,2.

Los dos siguientes parametros son las opciones que tomaran los objetos que metamos en la o las celdas. Para establecer ambos parametros vamos a tomar el objeto AttachOptions, las opciones posibles son Expand y Fill ambas son atributos de AttachOptions, se puede poner mas de una opcion sumandolas con la operacion "|". Finalmente los dos ultimos parametros son el espaciamiento que van a tener los objetos en la celda que lo insertemos. Un ejemplo de esto podria ser

		tabla.Attach(caja_de_texto, 2,3,2,3,
                         AttachOptions.Expand | AttachOptions.Fill,
                         AttachOptions.Fill, 0 , 0);
		

Las tablas son un objeto muy practico para colocar nuestros objetos ya que dentro de ellas nos permiten meter otros objetos como pueden ser otros widgets de disposicion ( como boxes ) o incluso otras tablas, para mas informacion mirar la documentacion de gtk en www.gtk.org


7.2. Creando layouts de tipo boxes

En estos tipos de layouts vamos a colocar los objetos de forma seguida, ya sea en disposicion vertical como en disposicion horizontal, asi que basicamente lo que tenemos que controlar es si queremos colocarlo por el final o por el principio. Por tanto tenemos dos tipos de boxes, horizontales y verticales


7.2.1. Horizontal Boxes ( HBox )

En este widget vamos a insertar los objetos siguiendo una disposicion horizontal, para ello primero tenemos que crear el objeto HBox de la siguiente forma

			Gtk.HBox disp_horiz = new Gtk.HBox(false, 10)
			

Donde el primer parametro indica si nuetros widgets van a ser colocados de forma homogenea, es decir que todos ocupen lo mismo. El segundo parametro indica el espaciado que vamos a dejar entre los objetos que coloquemos.

Una vez que tenemos el objeto creado ya nos disponemos a introducir nuestros widgets dentro de el, para ello fundamentalmente vamos a usar dos metodos, PackStart y PackEnd. Veamos un ejemplo de ellos

			disp_horiz.PackStart( objeto, expand, fill, padding )
			disp_horiz.PackEnd( objeto, expand, fill, padding )
			

Como vemos los parametros son similares asi que pasamos a explicarlos, el primero es el objeto que queremos insertar. El segundo parametro es si nuestro objeto va o no a ocupar todo el espacio restante dentro de nuestro widget box. El siguiente parametro ( fill ) controla si el espacio extra del box, va a ser rellenado con nuestro objeto o bien se va a utilizar como padding. Finalmente el ultimo parametro establece el espaciado que se va a dejar entre el objeto q estamos "empaquetando" y el siguiente objeto.

Una vez visto como introducimos objetos dentro del widget vamos a analizar la diferencia entre PackStart y PackEnd. El primero, PackStart, introduce los objetos de izquierda a derecha dentro del widget mientras que el segundo, PackEnd, es lo contrario; de derecha a izquierda.

Otros metodos importantes del objeto HBox pueden ser:

  • disp_horiz.PackEndDefaults(objeto). Introduce el objeto empezando por la derecha pero el resto de valores los toma por defecto

  • disp_horiz.PackStartDefaults(objeto). Igual que el anterior solo que esta vez empaquetamos de izquierda a derecha

  • disp_horiz.ReorderChild(objeto, posicion). Reordena un objeto dentro del layout


7.2.2. Vertical Box ( VBox )

Realmente este widget es un clon del anterior pero como se puede suponer diferente a la hora de empaquetar objetos. Su creacion es similar, por tanto

			Gtk.VBox disp_vert = new VBox(true, 10);
			

Como antes el primer parametro indica si la disposicion va a ser homogenea y el segundo el espaciado entre objetos insertados. Los principales metodos como antes van a ser PackStart y PackEnd, los cuales tendran los mismos parametros. La diferencia es que con el metodo PackStart los objetos se insertaran de arriba a abajo y usando PackEnd los objetos se insertaran comenzando por el limite inferior hasta el superior. Un ejemplo de codigo de todo esto puede ser:

			disp_vert.PackStart(boton, true, false, 10);
			disp_vert.PackEnd(caja_texto, false, false, 0);
			

Los metodos a destacar son identicos al caso anterior ya que ambos boxes derivan de la clase Box que es la que aporta esos metodos.


8. Conectando los eventos

Muy bien ahora ya tenemos nuestros botones y etiquetas y todo creado y ¿ahora que? pulso en un boton pero no sale nada. Aqui es donde entra en juego este capitulo. Cada objeto tiene unos eventos predeterminados, como por ejemplo un boton tiene el evento Clicked que se lanza cuando se pulsa. En el capitulo no nos vamos a centrar en profundidad en los eventos ya que es un tema amplio dentro de gtk# pero si seremos capaces de hacer cosas cuando se produce el evento.

La conexion de eventos en gtk# es similar a lo que haciamos en gtk+ con las funciones signal_connect. Veamos un ejemplo de codigo en gtk#

		boton.Clicked += new EventHandler (boton_pulsado);
		

Con esta linea de código lo que hacemos es ligar el evento Clicked del objeto boton a un manejador de eventos. Este manejador lo creamos para la ocasion y el constructor recibe como parametro la funcion que se va a encargar de gestionar el evento

Al igual que en el capitulo que creabamos la ventana aqui usamos el operador "+=" para significar que podemos añadir mas de un controlador para un evento predeterminado


9. Introduciendo una sencilla barra de menu

9.1. Los principales objetos que entran en juego

Para crear nuestra barra de menu vamos a usar principalmente 3 objetos, estos son: Menu, MenuBar y MenuItem. Como se puede deducir MenuBar es el objeto que define la Barra en si; el objeto Menu es el que representa los diferentes menus que puede haber dentro de la barra ( Archivo, Edicion... ) y finalmente MenuItem son las opciones que hay dentro de los menus


9.2. Creando la barra de menu

Una vez comprendido el uso de los tres objetos parece facil crear una barra de menus; por tanto el primer paso debe ser crear la propia barra de menu

	            MenuBar menu = new MenuBar();
                  

Bien, ya tenemos la barra de menu creada, ahora vamos a crear el menu "File"

                    Menu menu_file = new Menu()
	            MenuItem file_item = new MenuItem("_File");
                  

Con estas dos llamadas hemos creado el menu y el item que va a ser el que nosotros veamos yque va a poner, como queda claro, "File". El siguiente paso es decirle al menu que use el Item menu para su representacion; para ello

                    file_item.Submenu = menu_file;
                  

Ya tenemos creado el item, ahora el siguiente paso es crear todo lo que vayamos a meter dentro del menu "File", para el ejemplo vamos a insertar el Item "exit", por tanto necesitamos crear un objeto de tipo MenuItem e insertarlo dentro del menu "File"

	            MenuItem exit_item = new MenuItem("exit");
                    menu_file.Append(exit_item);
                  

Con estas dos sentencias ya tenemos nuestro menu "File" creado, ahora el siguiente paso es añadirlo a la barra de menu, lo que hacemos con

                    menu.Append(file_item);
                  

Ahora debemos añadir el callback para el Item exit, para que cuando pulsemos sobre el, se realize alguna opcion; en ese caso salir de la aplicacion. Para ello

	            exit_item.Activated += new EventHandler(exit_func);
                  

Como se ve, para hacer esto lo unico que tenemos que hacer es indicar que el evento Activated se controle con el EventHandler que creamos en esta sentencia; lo que realmente hace es llamar a la funcion exit_func.


9.3. Finalizando

Ahora ya tenemos creado el menu incluidos los controladores de eventos, el ultimo paso consiste en incluirlo en la ventana, para ello debemos empaquetarlo como si fuera un boton, una caja de texto o cualquier otro widget; por ejemplo si usaramos un vbox para empaquetar los widgets de la ventana pondriamos

	            VBOX.PackStart(menu, false, false, 0 );
                  

10. Conclusiones

Como se puede ver gtk# mejora gtk+ en la facilidad de uso. Dada la orientacion a objetos que nos ofrece gtk#, todo es bastante mas sencillo y conceptualmente mas comprensible. Se puede decir que es mucho mas facil programar con gtk# que con gtk+, aunque no conviene olvidarse de gtk+ ya que gtk# esta construido a partir de este y por lo tanto la api y la forma de uso es similar

La creacion de interfaces graficas con esta nueva variante es sencilla, pero sigue siendo tediosa, para ello esta libglade que se encarga de importar las interfaces creadas con glade dentro de nuestro programa

Como se ha visto el tutorial no cubre los aspectos mas complejos de gtk, de hecho tampoco era ese el objetivo. Con el tutorial se pretendia dar una vision introductoria al desarrollo de aplicaciones con gtk# y se ha tratado de hacer ver la integracion entre las dos APIs, como complemento al tutorial se distribuye una aplicacion que funciona como conversor de medidas. La podeis encontrar aqui