You are on page 1of 16

Dpto.

de Lenguajes y Sistemas Informticos Escuela Tcnica Superior de Ingeniera Informtica

Desarrollo de aplicaciones con Fox


Autores: Francisco Javier Melero Rus Domingo Martn Perandrs

Curso 2007/08

ndice general
ndice General 1. Descarga e Instalacin de FOX 1.1. Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2. Instalacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3. La clase FXApp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4. Creando la aplicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.1. Lo mnimo para usar OpenGL . . . . . . . . . . . . . . . . . . . . 1.5. Gestin de eventos en FOX . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.1. Identicadores de mensajes . . . . . . . . . . . . . . . . . . . . . 1.5.2. Gestores de eventos . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6. Dibujando con OpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.7. En resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. Eventos de teclado y ratn en FOX 2.1. Eventos de teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2. Eventos de ratn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 5 5 5 6 7 8 9 9 10 11 12 13 13 14

F.J. Melero y D. Martn c

Captulo 1

Descarga e Instalacin de FOX


1.1. Introduccin

Fox es una biblioteca para la construccin de Interfaces Grcas de Usuario, desarrollada ntegramente en C++. Inicialmente fue desarrollada para Linux, pero con el paso del tiempo, el mbito de este proyecto se volvi ms ambicioso. Los objetivos actuales son hacer FOX totalmente independiente de la plataforma, de forma que los programas desarrollados con FOX compilen en cualquier plataforma. Entre las caractersticas de FOX podemos destacar: Facilidad de desarrollo Basado en C++ Proporciona componentes para el desarrollo de interfaces grcos de usuario: Iconos e imgenes Ayudas contextuales Barras de herramientas otantes Componentes OpenGL Soporte para portapapeles Drag and Drop Interfaz grco auto-actualizable Sistema de eventos basado en paso de mensajes Multiplataforma

1.2.

Instalacin

Desde la web http://www.fox-toolkit.com se puede descargar la ltima versin estable de la biblioteca FOX. Tras descargar el archivo .tgz , se descomprime en la ubicacin que queramos (por ejemplo, en /usr/lib ). De ahora en adelante, denominaremos FOXDIR a esta localizacin.
F.J. Melero y D. Martn c

Descarga e Instalacin de FOX La instalacin es sencilla. Ejecutamos el script de conguracin: usuario@localhost: (FOXDIR)>./configure

Despus de que configure termine, tan slo hay que ejecutar make para construir la biblioteca, y make install para instalarla. Tras la instalacin, podemos ejecutar fox-config con la opcin -libs lo que nos devuelve las opciones de compilacin relativas a las bibliotecas. usuario@localhost: >fox-config -libs
-L/usr/local/lib -L/usr/X11R6/lib -lXext -lX11 -lXcursor -lXrandr -lFOX1.4 -ldl -lpthread -lrt -ljpeg -lpng -ltiff -lz -lbz2 -lm -lGL -lGLU

Para utilizar FOX, tan slo hay que incluir "fx.h", y para los programas 3D "fx3d.h". Si queremos usar smbolos del teclado, se incluir tambin "fxkeys.h". Para mantener la portabilidad del cdigo, se recomienda no incluir ningun archivo de cabecera de las X Window. Por supuesto, se seguir necesitando incluir el resto de cabeceras del sistema ("stdio.h", "gl.h", etc). Vamos a analizar la estructura bsica de un programa en FOX, as como su mecanismo de paso de mensajes (aunque sin entrar en profundidad).

1.3.

La clase FXApp

Todo programa es gestinado por un objeto aplicacin, que controla la cola de mensajes, las seales, la actualizacin de la interfaz grca y otras caractersticas del sistema. Cada programa que usa FOX tiene exactamente una instancia de la aplicacin. Todo programa comienza con la creacin de un objeto FXApp, antes incluso de la creacin del interfaz grco. Adems, el objeto FXApp es normalmente el ltimo en ser eliminado de memoria. En el cdigo que se muestra a continuacin, el objeto aplicacion se crea al comienzo, y es destruido automticamente cuando el programa termina. Adems, cuando el objeto aplicacin es destruido, todas las ventanas y dems recursos que contiene se destruyen. #include "fx.h" int main(int argc,char **argv) { // To create the application FXApp application("GLTest","FoxTest"); // Open the display application.init(argc,argv); // Make window new MiProgramaOpenGL(&application,500,500); // Create the applications windows
F.J. Melero y D. Martn c

1.4 Creando la aplicacin application.create(); // Run the application return application.run(); }

En la primera lnea de la funcin main, se crea el objeto aplicacin. El constructor tiene dos parmetros, el nombre de la aplicacin y el nombre del creador (la empresa, por ejemplo). Estos datos se utilizan para establecer los valores de registro de la aplicacin. La siguiente lnea de cdigo inicializa el objeto aplicacion, con los parmetros que se hayan introducido en la lnea de comandos. La siguiente lnea crea una ventana principal, de nuestra clase MyProgramaOpenGL (ver seccin siguiente), pasndole un puntero al objeto aplicacin, as como las dimensiones de la ventana. La llamada a la funcin create() del objeto aplicacion construye el rbol de widgets del interfaz grco, es decir, crea los recursos necesarios en el sistema (X Server o Windows GDI), para pasar de un conjunto de estructuras de datos en C++ a una aplicacin real capaz de recivir eventos y dibujar en la pantalla. La llamada nal a run() inicia el bucle principal del programa. Una aplicacin tpica no saldr de este bucle hasta que el usuario la cierre.

1.4.

Creando la aplicacin

Como se ha visto anteriormente, necesitamos una ventana que d soporte grco a toda la aplicacin. Esta ventana principal deriva de la clase FXMainWindow. Si necesitramos ms de una ventana para nuestra aplicacin, tendramos que crearlas derivndolas de FXDialogBox o FXTopWindow. class MiProgramaOpenGL : public FXMainWindow {

// Macro para las declaraciones de la jerarqua FXDECLARE(MiProgramaOpenGL) protected: MiProgramaOpenGL(){} public: MiProgramaOpenGL(FXApp *a, int width, int height); virtual void create(); }; La macro FXDECLARE(MiProgramaOpenGL) declara cierto nmero de funciones miembro que cada objeto derivado de FXObject (como es el caso de FXMainWindow ) debe tener. Para satisfacer la condicin de serializacin que FOX impone a los objetos, tenemos que crear el constructor por defecto ( MiProgramaOpenGL()), aunque no tuviramos intencin de hacerlo.
F.J. Melero y D. Martn c

Descarga e Instalacin de FOX

El constructor de nuestra clase tiene como parmetros: un puntero al objeto aplicacin que lo gestionar (se explica en el siguiente apartado) y las dimensiones, ancho y alto, del objeto. El mtodo create() es una funcin virtual llamada por el sistema. La mayora de los componentes FOX tienen esta funcin create. Los widgets de FOX tienen un proceso de creacin en dos etapas: en primer lugar, se construyen los widgets haciendo uso de los constructores C++ en el lado cliente de la aplicacin; cuando se ha construido el rbol completo de componentes y se ha establecido la conexin con el gestor de ventanas, se invoca el mtodo create() del objeto aplicacin, que a su vez llamar a los mtodos create de cada uno de los componentes. sta es la clase principal de nuestro GUI, a la que le iremos aadiendo componentes para aumentar su funcionalidad.

1.4.1.

Lo mnimo para usar OpenGL

En primer lugar, hay que incluir la cabecera especial para la visualizacin 3D de FOX: #include "fx3d.h" Puesto que queremos trabajar con OpenGL, debemos denir una estructura canvas y otra visual. La primera se puede entender como la zona donde se dibuja, mientras que la segunda es la encargada de tomar el dibujo y representarlo. Por tanto, para poder utilizar OpenGL es necesario haber denido un lienzo o zona de dibujo (canvas) y haber establecido las caractersticas de dicha zona de dibujo. El lienzo se dene mediante la clase FXGLCanvas, mientras que las caractersticas visuales se denen mediante FXGLVisual. Aadimos a nuestra clase MiProgramaOpenGL estos dos componentes:
private: FXGLCanvas FXGLVisual *GL_canvas; *GL_visual; // GL Canvas to draw into // OpenGL visual

En la estructura jerrquica de FOX, el lienzo debe depender de algn objeto, en este caso de la ventana principal. En la denicin del canvas, en el constructor de nuestra ventana principal, adems de la ventana que lo contiene, hay que indicar las caractersticas:
GL_canvas=new FXGLCanvas(this,GL_visual,this,ID_CANVAS, LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT);

El primer parmetro es el puntero a la ventana que contiene al canvas; el segundo, el controlador de las caractersticas visuales; el tercer parmetro es el objeto destino de los mensajes que genere, y el cuarto el identicador que se le asigna a los mensajes que el canvas genere. Las caractersticas de visualizacin del lienzo dependen de la aplicacin, no de la ventana: GL_visual=new FXGLVisual(getApp(),VISUAL_DOUBLEBUFFER);
F.J. Melero y D. Martn c

1.5 Gestin de eventos en FOX

Con getApp() obtenemos el puntero a la aplicacin que estamos ejecutando, y el segundo parmetro es la conguracin visual que vamos utilizar, en este caso, con doble buffer. Pero con esto que hemos visto hasta ahora no es suciente para echar a andar la aplicacin. Los eventos, y el sistema de paso de mensajes en FOX es algo fundamental, y si se ignoran completamente, el enlazador devolver error.

1.5.

Gestin de eventos en FOX

FOX gestina los eventos que ocurren en el sistema mediante un sistema de paso de mensajes enviados a un cierto objeto . En el caso de la aplicacin de ejemplo que estamos desarrollando, el receptor de los mensajes ser la clase MiProgramaOpenGL. Para gestionar estos mensajes, debemos aadir funciones miembro que capturen estos mensajes y realicen alguna accin de respuesta. Todos los manejadores de mensajes en FOX tienen la misma sintaxis:
long onAlgunaAccion ( FXObject* emisor, FXSelector sel, void* ptr);

Donde: emisor es el objeto que le ha enviado el mensaje a la clase. sel es el selector, una combinacion de un tipo de mensaje y un identicador de mensaje, que identica la accin que se ha realizado. ptr es un puntero a ciertos datos relacionados con el evento; normalmente, apunta a la estructura FXEvent que contiene al evento que ha provocado el mensaje.

1.5.1.

Identicadores de mensajes

Lo ms normal es que la ventana que denamos necesite nuevos identicadores de mensajes. Un mensaje consiste en un tipo y en un identicador. El tipo dene qu ha ocurrido; el identicador dene el origen del mensaje. Aunque sepamos el objeto que nos va a enviar los mensajes, es posible que podamos recibir el mismo mensaje de distintos orgenes, por lo que el identicador es conveniente. Como nuestra clase MiPrimerProgramaOpenGL deriva de FXMainWindow, tambin entiende todos los mensajes ya conocidos por la clase base. Todos los tipos de mensaje se declaran en una enumeracin, y para denir los tipos de mensajes propios de la clase que estamos deniendo, hay que evitar machacar valores de mensajes de la clase base. sto se consigue de la siguiente manera: enum { ID_CANVAS=FXMainWindow::ID_LAST, ID_LAST, }; Nuestra clase MyProgramaOpenGL, por ahora, slo gestionar los mensajes con origen en el canvas, ID_CANVAS. Se puede observar cmo la enumeracion de identicadores propios de nuestra ventana comienza en el valor que termina la enumeracin de identicadores de mensajes de la clase base FXMainWindow. Toda enumeracin debe terminar
F.J. Melero y D. Martn c

10

Descarga e Instalacin de FOX

con el nombre ID_LAST, de forma que cualquier derivacion de la clase, pueda denir sus propios identicadores de mensaje de la misma forma. De esta forma, cada vez que aadamos un identicador de mensaje a cualquiera de nuestras clases, el compilador se encargar de renumerarlas.

1.5.2.

Gestores de eventos

Una vez que tenemos denidos los eventos propios de cada una de las clases, hay que denir el mapa de mensajes, que no es ms que una tabla que relaciona un tipo de mensaje, un identicador de mensaje y la funcin miembro que se encarga de tratar el evento. Con un mapa de mensajes, podemos enviar cualquier mensaje a cualquier objeto que derive de FXObject. La denicin de la tabla se realiza de la siguiente manera:
FXDEFMAP(clase) tabla_clase[]= { FXMAPFUNC(tipo_mensaje, identificador_mensaje, metodo_manejador(objetivo)) };

En el caso concreto de esta primera aplicacin, la tabla se denir de la siguiente forma:


FXDEFMAP(MiProgramaOpenGL) MiProgramaOpenGL_map[]= { FXMAPFUNC(SEL_PAINT, MiProgramaOpenGL::ID_CANVAS, MiProgramaOpenGL::on_expose) };

Esto se traducira como "si se produce un mensaje del tipo SEL_PAINT cuyo origen sea el objeto relacionado con el identicador ID_CANVAS de la clase MiProgramaOpenGL, que se encargue de responderle el mtodo on_expose de dicha clase" En concreto, el mtodo on_expose se declara, segn la especicacin vista anteriormente como:
long on_expose(FXObject* sender,FXSelector sel,void* ptr);

Hay que indicar varias cosas sobre esta tabla; en primer lugar, puede haber varios mensajes con el mismo identificador pero con distinto tipo. El tipo de los mensajes indica qu ha ocurrido, por ejemplo, SEL_LEFTBUTTONPRESS signica que el botn izquierdo del ratn acaba de ser pulsado. El identicador del mensaje da a conocer el origen del mismo. FOX dene un variado conjunto de tipos de mensajes, cada uno de ellos con su signicado especco. Es conveniente poner los mensajes o eventos que se producen ms habitualmente al principio, ya que de esta forma sern gestionados ms rpido. Finalmente, hay que incluir toda esta informacin en la jerarqua de componentes que maneja FOX. sto se hace mediante la macro FXIMPLEMENT , de la siguiente forma:

FXIMPLEMENT(clase\_hijo, clase\_padre, mapa, nmero de elementos en el mapa)}\\


F.J. Melero y D. Martn c

1.6 Dibujando con OpenGL

11

La macro ARRAYNUMBER nos devuelve el tamao de la tabla de mensajes, sin tener que preocuparnos de este dato cuando la cambiemos. En nuestro caso, la inclusin de estos datos se hara con:

FXIMPLEMENT(MiProgramaOpenGL,FXMainWindow, MiProgramaOpenGL\_map, ARRAYNUMBER(MiProgramaOpenGL\_map))\\

IMPORTANTE: Cada objeto FOX debe invocar a FXDECLARE en su chero de cabecera, y a FXIMPLEMENT en su chero de implementacin. Algunos gestores de mensajes devuelven 1, y otros 0. El convenio es que si el mensaje ha sido recogido y se ha actuado en consecuencia, es decir, se ha procesado normalmente, se devuelve 1. En otro caso, se devuelve 0.

1.6.

Dibujando con OpenGL

Acabamos de indicarle a FOX que ante el evento SEL_PAINT invoque al mtodo on_expose de la clase MiProgramaOpenGL. Qu har esta funcin miembro? Simplemente ejecutar las sentencias necesarias para que se dibuje algo en el canvas. Eso s, no tiene sentido que el gestor de mensajes .entienda"de cmo dibujar, sino que lo ms lgico es que de ello se encargue otro mtodo: draw_scene() (por llamarla de alguna manera).
long MiProgramaOpenGL::on_expose(FXObject* sender,FXSelector sel,void* ptr) { draw_scene(); return 1; }

De esta forma aislamos la gestin de eventos de lo que se podra denominar l ncleo"de nuestra aplicacin. Veamos una funcin draw_scene bsica:
void MiProgramaOpenGL::draw_scene() { GLint width = GL_canvas->getWidth(); GLint height = GL_canvas->getHeight(); GLdouble aspect = height>0 ? width/height : 1.0; // Activar el contexto del canvas en el que queremos dibujar GL_canvas->makeCurrent(); // **** INICIO "SECCION OPENGL" ********* glViewport(0,0,width, height); glClearColor(1.0,1.0,1.0,1.0); glClear(GL_COLOR_BUFFER_BIT); draw_axis(); draw_object(); // **** FIN "SECCION OPENGL" ********* // Si el dibujado se hace con doble buffer, intercambiar if(GL_visual->isDoubleBuffer()) GL_canvas->swapBuffers();
F.J. Melero y D. Martn c

12

Descarga e Instalacin de FOX

// Desactivar el contexto del canvas GL_canvas->makeNonCurrent(); }

Como se puede ver, diere poco de la funcin de dibujo utilizada en la prctica anterior usando GLUT. Lo nico especco de FOX, y donde realmente se aprecia la orientacin a objetos, es en la forma que se obtiene la informacin, tanto del viewport como de las propiedades o caractersticas de dibujo. La secuencia de hechos que se produce para dibujar es la siguiente:

1. FOX detecta que se tiene que dibujar 2. Mira en la ventana principal si tiene denido el evento SEL_PAINT 3. Si est denido, mira en la tabla de mensajes, toma el identicador y llama a la funcin correspondiente (en el caso que estamos viendo, on_expose ). 4. La funcin trata el evento.

1.7.

En resumen
Construir aplicaciones con FOX supone construir ms clases C++. Todas las clases relacionadas con el interfaz grco deberan derivar, directa o indirectamente, de FXObject. Si se disean bien, es muy fcil volver a utilizarlas en otro proyecto. FOX usa un sistema de gestin de eventos basado en el paradigma destinatario/mensajes. Cada gestor de mensajes tiene tres argumentos: el emisor del mensaje, el selectore del mensaje (tipo + id ) y un puntero que puede proveer informacin adicional sobre el mensaje. El tipo de mensaje identica la accin que ha ocurrido, mientras que el identicador informa del objeto que gener el mensaje, lo que hace cada mensaje nico. Para denir nuevos mensajes, utilizar enumeraciones. El primer mensaje nuevo de cada clase debe coincidir con el ID_LAST de la clase base. De esta forma conseguimos que el compilador asigne un nmero nico a cada mensaje. Durante los momentos en los que el programa se encuentra inactivo, FOX actualiza automticamente cada control, preguntando a cada control su estado. El mensaje enviado a cada control es del tipo SEL_UPDATE. Los atajos de teclado para cada botn se hacen anteponiendo a la letra rpida el carcter & . Esta letra se subraya automticamente.

F.J. Melero y D. Martn c

Captulo 2

Eventos de teclado y ratn en FOX


El programa hecho en la prctica anterior, aparte de para mostrar la estructura bsica de una aplicacin FOX, sirve de poco. Vamos a aadirle cierta funcionalidad al programa, para que responda a las pulsaciones de teclas y a los movimientos del ratn, de igual forma que se hizo con glut en la prctica anterior. En ambos casos, el funcionamiento es sencillo, tan slo hay que incluir los eventos en la tabla de mensajes con su gestor correspondiente.

2.1.

Eventos de teclado

Vamos a posibilitar el cambio de proyeccin pulsando F1 y permitiremos ocultar/mostrar los ejes coordenados pulsando la tecla a. Para la gestin de teclado hay dos tipos de mensaje: SEL_KEYPRESS y SEL_KEYRELEASE, por lo que las nuevas entradas en la tabla de mensajes podran ser:
FXMAPFUNC(SEL\_KEYPRESS, MiProgramaOpenGL::ID\_CANVAS, MiProgramaOpenGL::on\_key\_press) \\ FXMAPFUNC(SEL\_KEYRELEASE, MiProgramaOpenGL::ID\_CANVAS, MiProgramaOpenGL::on\_key\_release)

Para comprobar qu tecla ha sido la que ha generado el evento, se hace uso del tercer parmetro del gestor de mensajes, que, segn vimos anteriormente, es un void*: long MiProgramaOpenGL:: on_key_press ( FXObject* emisor, FXSelector sel, void* ptr) { FXEvent *event=(FXEvent *) ptr; switch (Event->code) { case KEY_F1: toggleProjection();break; case KEY_a: toggleAxis();break; case KEY_Escape: exit(0);break; } draw_scene(); }
F.J. Melero y D. Martn c

14

Eventos de teclado y ratn en FOX


1

Como se puede apreciar, lo nico que se hace es examinar el cdigo de la tecla realizar la llamada a la funcin correspondiente.

Hay que resaltar que si se omite la ltima lnea, no se redibuja la escena hasta que no se produzca un evento del tipo SEL_PAINT, por lo que pasaran inadvertidos los cambios que hicisemos.

2.2.

Eventos de ratn

La gestin de los eventos de ratn se realiza de la misma manera: tan slo hay que indicar en la tabla de mensajes qu funcin se encarga de gestionar los distintos tipos de mensajes del ratn. Con el ratn, vamos a posibilitar la manipulacin de la cmara, rotando la escena con respecto al origen de coordenadas mundiales segn el movimiento de ratn. Para ello, vamos a controlar el momento en que se pulsa y se libera el botn izquierdo del ratn, y cuando se mueve el ratn:
FXMAPFUNC(SEL_LEFTBUTTONPRESS, MiProgramaOpenGL::ID_CANVAS, MiProgramaOpenGL::on_cmd_left_button_press), FXMAPFUNC(SEL_LEFTBUTTONRELEASE, MiProgramaOpenGL::ID_CANVAS, MiProgramaOpenGL::on_cmd_left_button_release), FXMAPFUNC(SEL_MOTION, MiProgramaOpenGL::ID_CANVAS, MiProgramaOpenGL::on_cmd_motion)

Otros eventos posibles que genera el objeto Canvas con el ratn son: SEL_RIGHTBUTTONPRESS, SEL_RIGHTBUTTONRELEASE, SEL_MIDDLEBUTTONPRESS y SEL_MIDDLEBUTTONRELEASE.
long MiProgramaOpenGL:: on_cmd_left_button_press(FXObject *sender,FXSelector message,void *Ptr) { FXEvent *event=(FXEvent *) Ptr; initX=event->click_x; initY=event->click_y; return 1; } long MiProgramaOpenGL:: on_cmd_left_button_release(FXObject *sender,FXSelector message,void *Ptr) { FXEvent *event=(FXEvent *) Ptr; initX=-1; initY=-1; return 1; } long MiProgramaOpenGL:: on_cmd_motion(FXObject *sender,FXSelector message,void *Ptr) {
1

todos los cdigos de teclas se pueden consultar en el chero cabecera <fxkeys.h>

F.J. Melero y D. Martn c

2.2 Eventos de ratn


FXEvent *event=(FXEvent *) Ptr; if (initX!=-1) { angleY+=90*float(event->win_x - initX)/float(GL_canvas->getWidth()); angleX+=90*float(event->win_y - initY)/float(GL_canvas->getHeight()); initX=event->win_x; initY=event->win_y; } draw_scene(); return 1; }

15

F.J. Melero y D. Martn c