Vous êtes sur la page 1sur 26

Dpto.

de Lenguajes y Sistemas Informticos Escuela Tcnica Superior de Ingeniera Informtica

Desarrollo de aplicaciones con Qt


Autor: Francisco Javier Melero Rus

Curso 2007/08

ndice general
ndice General 1. Introduccin al uso de Qt 1.1. Qu es Qt? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1. Cmo est disponible Qt? . . . . . . . . . . . . . . . . . . . . . . 1.1.2. Cmo obtener e instalarse Qt? . . . . . . . . . . . . . . . . . . . 1.2. Hola Mundo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1. Lnea a lnea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.2. Compilacin y ejecucin . . . . . . . . . . . . . . . . . . . . . . . 1.3. Algo ms complejo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1. Lnea a lnea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4. Gestin de eventos en Qt . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.1. Seales y slots . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2. Un ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5. Qt y OpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.1. Denicin de VisorOpenGL . . . . . . . . . . . . . . . . . . . . . 1.5.2. Implementacin de VisorOpenGL . . . . . . . . . . . . . . . . . . 1.5.3. Inicializacin de OpenGL . . . . . . . . . . . . . . . . . . . . . . 1.5.4. Redimensin del Viewport . . . . . . . . . . . . . . . . . . . . . . 1.5.5. Dibujando la escena . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.6. Gestin del ratn . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.7. Gestin del teclado . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.8. Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 5 5 6 6 7 8 9 9 10 11 11 12 14 15 17 18 19 19 22 22 23

F.J. Melero c

Captulo 1

Introduccin al uso de Qt
1.1. Qu es Qt?

Es una librera para desarrollar aplicaciones con interfaz grco de usuario con C++. En realidad, como todo software que se precie, no son slo unas clases empaquetadas y listas para enlazar con ellas en tiempo de compilacin, sino que Qt proporciona una serie de herramientas y documentacin imprescindibles para el desarrollador: Qt Designer, para crear dilogos e interfaces grcamente (echadle un vistazo, es bastante bueno), Qt Linguist, una herramienta para crear aplicaciones multilinges Qt Assistant, un generador de documentacin qmake, un generador de Makeles multiplataforma. Las aplicaciones ms populares que usan Qt son por supuesto las de KDE. Las libreras de KDE extienden y complementan a las de Qt, as que las aplicaciones KDE dependen de Qt. Hay otras aplicaciones interesantes hechas con Qt, pero que no son KDE. Las ms populares son Scribus, Skype y muchas otras.

Figura 1.1: Arquitectura de Qt


F.J. Melero c

Introduccin al uso de Qt

El principal motivo por el que Qt se ha hecho tan popularidad es su propiedad de ser multiplataforma, esto es, con un mismo cdigo, las aplicaciones se pueden compilar sin tocar una sola lnea en distintos sistemas operativos. Esto es posible porque los desarrolladores de Qt se han preocupado de generar diversas versiones de la librera: Qt/Windows (MS Windows 95/98/Me, NT4, 2000 y XP), Qt/X11 (Linux, *BSD, Solaris, HP-UX, IRIX, AIX, y otras variantes de UNIX que usen X11), y Qt/Mac (Apple Mac OS X). En denitiva, se programa una sola vez, y se compila contra la variante apropiada de Qt. Las libreras Qt no slo estn disponibles para programar en C++, sino que hay soluciones para ser usadas con Python (PyQt), Java (Jambi), Javascript, etc...

1.1.1.

Cmo est disponible Qt?

Qt funciona con lo que se denomina licencia dual: Licencia comercial: es la apropiada si se van a crear aplicaciones propietarias y no se va a distribuir y compartir el cdigo. Licencia Open Source: si la aplicacin que se desarrolle va a hacer uso de alguna de las licencias compatibles GPL. La versin que viene en las distribuciones de linux, es la Qt Open Source Edition, que est cubierta bajo la GPLv2 (y por tanto, con cdigo fuente includo), y que est disponible para X11, Windows y Mac. Esta edicin incluye la ltima versin de Qt, y todas sus caractersticas. Por las caractersticas de la licencia GPLv2, si optamos por la Qt Open Source Edition, nuestro cdigo, al enlazarse contra ellas, deber estar bajo una de las licencias libres vlidas (GPL, LGPL, BSD, etc.). En denitiva, Qt no exige dinero salvo que el desarrollador obtenga benecio econmico del uso de su librera, en cuyo caso es obligatorio -y se podra decir que lgico y tico.agradecer.econmicamente el esfuerzo que han realizado para proporcionarnos la librera. La versin ms reciente disponible en formato Open Source es la 4.3.3.

1.1.2.

Cmo obtener e instalarse Qt?

En Linux es trivial su instalacin, ya que se instalan con el entorno de ventanas KDE. Si an as no tuvisemos instalada la librera, nos la podemos descargar de la web de Trolltech (la URL exacta para la versin linux es http://trolltech.com/developer/downloads/qt/x11 ) y seguir las instrucciones indicadas en el archivo (ftp://ftp.trolltech.com/qt/source/INSTALL). Podemos tanto descargarnos los fuentes y compilar e instalar las libreras, como bajarnos uno de los paquetes precompilados preparados especcamente para nuestra distribucin de Linux (Red Hat, Suse, Debian, etc... ).Al ser trivial sta ltima opcin, en esta gua slo vamos a detallar los pasos a seguir para una instalacin desde los fuentes. Instalacin de los fuentes Nos descargamos los fuentes de alguno de los repositorios disponibles, y lo copiamos a un directorio temporal (p.ej. /tmp). El proceso se describe en pocas lneas: 1. Descomprimir el tgz:
F.J. Melero c

1.2 Hola Mundo % cd /tmp % gunzip qt-x11-opensource-src-4.3.3.tar.gz % tar xvf qt-x11-opensource-src-4.3.3.tar

Esto crear el directorio /tmp/qt-x11-opensource-src-4.3.3 con los archivos necesarios. 2. Compilacin e instalacin % % % % cd /tmp/qt-x11-opensource-src-4.3.3 ./configure make su -c "make install"

Ntese que la ltima lnea est solicitando ejecutar make install como root, para poder alojar las libreras en /usr/local/Trolltech/Qt-4.3.3. Obviamente, nos pedir contrasea. Podemos pedirle que se instalen en otra ruta indicando con -prex la ruta deseada: sudo -c "make install -prefix /ruta/deseada" 3. Conguracin de las variables del sistema. En el archivo /.profile (si la shell es bash, ksh, zsh o sh), hay que aadir: PATH=/usr/local/Trolltech/Qt-4.3.3/bin:$PATH export PATH En ~/.login (si la shell es csh o tcsh), se aade la siguiente lnea setenv PATH /usr/local/Trolltech/Qt-4.3.3/bin:$PATH En las aulas de prcticas puede ser necesario extender la variable de entorno LD_LIBRARY_PATH incluyendo la ruta /usr/local/Trolltech/Qt-4.3.3/lib.

1.2.

Hola Mundo

Como todo en informtica, hay que empezar con el "Hola Mundo". Se trata de realizar un programa mnimo que tan slo saque por pantalla la famosa frase. He aqu el cdigo #include <QApplication> #include <QLabel> int main (int argc, char *argv[]) { QApplication app(argc,argv); QLabel *etiqueta = new QLabel ("Hola Mundo"); etiqueta->show(); return app.exec(); }
F.J. Melero c

Introduccin al uso de Qt

1.2.1.

Lnea a lnea

#include <QApplication> Incluimos la denicin de la clase QApplication. Debe haber uno y slo un objeto QApplication en cada programa que haga uso de un interfaz grco con Qt. La clase QApplication gestiona temas tales como el cursor del ratn, la fuente por defecto, etc... #include <QLabel> Incluimos la denicin de la clase QLabel. Todas las clases de Qt comienzan con la letra Q, y para cada clase de la API, hay un chero .h con el mismo nombre que contiene su denicin. Al ser C++, no es necesario poner la extensin. QLabel es un componente visual que sirve para mostrar texto esttico, es decir, que el usuario no puede modicar el contenido. Tiene sus propias propiedades (color de fondo, tamao de la fuente, dimensiones, etc...) como cualquier otro componente (widget) de la librera. int main(int argc, char *argv[]) Como cualquier programa en C++, todo comienza con el main(). En la mayora de los casos, el main() slo tiene que realizar un par de operaciones de inicializacin antes de pasar el control a la librera Qt, que es quien se encarga de detectar y gestionar las acciones del usuario mediante eventos. QApplication app(argc, argv); El objeto app es la instancia de la QApplication. Se crea aqu. Los argumentos sirven para pasar opciones desde lnea de comandos, pero raramente se usan. Debe ser la primera lnea del main, o al menos, la primera en la que se haga referencia a algn elemento Qt. QLabel *etiqueta= new QLabel("Hola Mundo"); Creamos un objeto etiqueta, QLabel. El argumento del constructor es el texto que se va a mostrar. Como no le hemos indicado ninguna ventana como elemento padre (sera el segundo argumento), el componente en s se comportar como una ventana, con barra de ttulo, marco, etc... etiqueta->show(); Hay que distinguir entre la creacin de componentes y su visibilidad. En la lnea anterior hemos creado el objeto QLabel, pero ese objeto est en memoria. En ningn momento se le ha dicho que se visualice. Por eso se ejecuta el mtodo show(), para hacerlo visible. return app.exec(); } Aqu es cuando el main pasa el control a Qt. QCoreApplication::exec() acabar cuando se nalice la aplicacin. En QCoreApplication::exec(), Qt recibe y procesa los eventos del usuario y del sistema, y se encarga de gestionar la respuesta a stos segn lo que el programador haya determinado.
F.J. Melero c

1.3 Algo ms complejo

1.2.2.

Compilacin y ejecucin

Para compilar es necesario tener un makele. La forma ms fcil es usar la herramienta qmake que Qt proporciona. El proceso a seguir sera: Creamos un directorio (p.ej p00) vaco. Guardarmos ah el archivo con el cdigo anterior (p.ej. p00.cpp) Creamos un archivo de proyecto (extensin .pro) y a continuacin creamos un makele adaptado a la plataforma en la que estemos: % qmake -project % qmake Compilamos: % make Y listo! Ya podemos ejecutar nuestro primer programa Qt: ./p00

1.3.

Algo ms complejo

Vamos a introducir el concepto de los eventos de usuario. Para ello crearemos una ventana con un botn que, al pulsarlo, nalizar la ejecucin:

#include #include #include #include #include

<QApplication> <QFont> <QPushButton> <QLabel> <QWidget>

int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget window; window.resize(200, 120); QLabel etiqueta("Pulse para salir", &window); QPushButton salir("Salir", &window); salir.setFont(QFont("Times", 18, QFont::Bold)); salir.setGeometry(10, 40, 180, 40); QObject::connect(&salir, SIGNAL(clicked()), &app, SLOT(quit())); window.show(); return app.exec(); }

F.J. Melero c

10

Introduccin al uso de Qt

Figura 1.2: Captura del ejemplo

1.3.1.

Lnea a lnea

#include <QWidget> Incluimos la clase base de todos los componentes de Qt, <QWidget>. QWidget window; Se crea un componente bsico, que va a ser utilizado como contenedor de todos los dems. Un componente que no es incluido en otros se denomina ventana. Se podra decir que los componentes o widgets son los ladrillos del interfaz de usuario: reciben los eventos de ratn y teclado, las seales del sistema, son los encargados de pintar la interfaz grca... Adems, un componente es recortado por su padre y por los que se superponen a l. Si no se especica otra cosa, es el gestor de ventanas quien se encarga de colocar la ventana de nuestra aplicacin en un lugar del escritorio. window.resize(200, 120); Denimos la ventana con 200 pxeles de ancho y 120 de alto. QLabel etiqueta("Pulse para salir", &window); QPushButton salir("Salir", &window); Creamos dos componentes: una etiqueta y un botn. Tanto la etiqueta, QLabel, como el botn, de la clase QPushButton, se crean con un segundo parmetro que es su componente padre (window). Un componente hijo siempre se visualiza dentro del rea ocupada por su ancestro y es recortado si excede de sus lmites. Por defecto, se localiza en la esquina superior izquierda de su elemento contenedor, en la posicin (0,0). En este ejemplo, la etiqueta queda en dicha posicin. salir.setFont(QFont("Times", 18, QFont::Bold)); Establecemos para el botn el tipo y tamao de la fuente. Para ello, se usa el mtodo QWidget::setFont(QFont) que recibe como parmetro un objeto del tipo QFont. ste objeto es creado con tres parmetros: la familia de la fuente ("Times"), el tamao (18) y el estilo (Qfont::Bold). salir.setGeometry(10, 40, 180, 40);
F.J. Melero c

1.4 Gestin de eventos en Qt

11

La funcin QWidget::setGeometry() tiene cuatro argumentos: los dos primeros son las coordenadas x e y de la esquina superior izquierda del componente (en este caso el botn). Los dos siguientes son el ancho y el alto del botn. El resultado es un botn que va desde la posicin (10,40) a la (190,80). QObject::connect(&salir, SIGNAL(clicked()), &app, SLOT(quit())); QObject::connect() es quiz la caracterstica ms importante de Qt. connect() es una funcin esttica en QObject, lo que quiere decir que su cdigo es compartido por todos los objetos, y gestiona una zona de memoria diferente. La llamada a connect() establece una conexin unidireccional entre dos objetos Qt (objetos que directa o indirectamente heredan de QObject). Cada objeto Qt puede tener ambas cosas, seales (para enviar mensajes, tambin denominados eventos) y slots (para recibir mensajes o eventos). todos los componentes o widgets son objetos Qt, ya que heredan de QWidget, que a su vez hereda de QObject. Cuando se genera la seal clicked() del botn salir, se ejecuta el mtodo quit() del objeto app, de forma que cuando se pulsa el botn, la aplicacin se acaba. Cabe decir que tanto esta seal como el slot estn predenidos en los objetos en cuestin, lo cual no impide que nosotros denamos nuestras propias seales y slots. window.show(); Al ordenar la visualizacin de la ventana, todos sus componentes hijos son automticamente visualizados, salvo aquellos que se hayan ocultado explcitamente mediante QWidget::hide().

1.4.

Gestin de eventos en Qt

Cuando se programa un interfaz grco de usuario, la forma de pensar y disear el programa cambia radicalmente a como se realiza secuencialmente. El usuario puede hacer cualquier cosa en cualquier momento, los botones pueden ser pulsados aleatoriamente, y el sistema debe responder a dichos eventos. Por otro lado, puede que la accin sobre un componente suponga un cambio en otro, o dicho de una forma ms general, debera ser posible que cualquier objeto se pudiera comunicar con cualquier otro. Por ejemplo, podramos querer que tras acabar una simulacin, apareciera una etiqueta en verde avisando del resultado correcto, o en rojo si es incorrecto. Libreras ms antiguas, como glut, consiguen esta comunicacin con callbacks. Un callback es un puntero a una funcin, por lo que cuando ocurre algn evento, se llama a dicha funcin para que lo procese. Esta tcnica tiene dos problemas: no hay garanta de que los argumentos que se pasen a la funcin sean del tipo adecuado y por otro lado hay un alto acoplamiento entre la funcin que realiza el procesamiento y la que ha de responder al evento generado por aquella.

1.4.1.

Seales y slots

En Qt una seal se emite cuando ocurre un evento en particular. Los componentes de Qt tienen muchas seales predenidas, pero podemos crear componentes derivados y aadir
F.J. Melero c

12

Introduccin al uso de Qt

Figura 1.3: Relaciones entre seales y eventos nuestras propias seales. Un slot es una funcin que es llamada como respuesta a una seal en concreto. Los componentes de Qt tienen muchos slots predenidos, pero lo normal es aadir nuestros propios slots para manejar aquellas seales que nos interesen. Este mecanismo garantiza seguridad en los tipos: los datos que genera la seal deben coincidir con los que recibe el slot. Adems, hay un muy bajo acoplamiento, ya que la clase que emite la seal no tiene por qu saber quin manejar despus la respuesta a dicha seal. Todas las clases que heredan de QObject o alguna de sus subclases (p.ej. QWidget) pueden tener seales y slots. Las seales se emiten cuando el objeto cambia su estado de forma que puede ser interesante para otros objetos. Digamos que el objeto slo notica que algo a pasado, pero le da exactamente igual si a alguien le interesa -hay un slot que lo gestiona- o no. Los slots se usan para recibir seales, pero en resumen son funciones miembro normales. De la misma forma que un objeto no sabe si alguien recibir sus seales, un objeto no sabe si ser llamado alguno de sus mtodos como consecuencia de la generacin de una seal. Se pueden conectar varias seales a un mismo slot, y una seal se puede conectar a varios slots distintos. Incluso se pueden encadenar seales, para que una se produzca como reaccin a la primera.

1.4.2.

Un ejemplo

Una clase contador bsica podra ser algo as: class Contador { public: Contador() { m_value = 0; } int value() const { return m_value; } void setValue(int value); private:
F.J. Melero c

1.4 Gestin de eventos en Qt int m_value; }; La podemos convertir en un objeto Qt aadiendo unas pocas lneas de cdigo: #include <QObject> class Contador : public QObject { Q_OBJECT public: Contador() { m_value = 0; } int value() const { return m_value; } public slots: void setValue(int value); signals: void valueChanged(int newValue); private: int m_value; };

13

La idea es la misma, y los metodos pblicos son los mismos, pero le hemos aadido la propiedad de responder a eventos mediante seales y slots. Esta clase le cuenta al mundo exterior que su estado ha cambiado emitiendo la seal valueChanged(), y tiene un slot al que otros objetos podran enviar seales. Importante: Todas las clases que denan seales o slots deben incluir Q_OBJECT al principio de su declaracin, y deben derivar -directa o indirectamente- de QObject. Los slots son implementados por el programador. He aqu una posible implementacin del slot Contador::setValue(): void Contador::setValue(int value) { if (value != m_value) { m_value = value; emit valueChanged(value); } } La segunda lnea del if emite la seal valueChanged() con el nuevo valor como argumento. En el siguiente trozo de cdigo, creamos dos objetos Contador y conectamos la seal valueChanged() del primero al setValue() del segundo, usando QObject::connect(): Contador a, b;
F.J. Melero c

14

Introduccin al uso de Qt QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int))); a.setValue(12); b.setValue(48); // a.value() == 12, b.value() == 12 // a.value() == 12, b.value() == 48

Llamando a.setValue(12) emitimos la seal valueChanged(12), que b recibe en su slot setValue(), es decir, se llama a b.setValue(12). En ese instante, b emite la seal valueChanged -como todos los objetos Contador al ejecutar setValue-, pero como no se ha creado ninguna conexin entre su seal valueChanged y algn slot, la seal se ignora. Fijmonos que la funcin setValue() slo actualiza el valor y emita la seal si value != m_value. As evitamos bucles innitos si por ejemplo b.valueChanged() estuviese conectado con a.setValue(). Se emite una seal por cada conexin que haya. Es decir, si hubiese dos slots distintos para la misma seal, se emitira una seal para cada slot. Las seales no deben implementarse en el archivo .cpp, y nunca pueden devolver un valor. Siempre son void Los slots son funciones C++ normales y corrientes, con la nica caracterstica que pueden ser conectadas a seales. Esto tiene la peculiaridad de que una funcin privada nunca podr ser llamada por otro objeto mediante la ejecucin del mtodo, pero s mediante la respuesta a una seal.

1.5.

Qt y OpenGL

Vamos a crear un esqueleto potente para poder desarrollar todas las prcticas de la asignatura. El ncleo del interfaz del usuario son dos clases: la Ventana y el VisorOpenGL. Vamos a ver la estructura del cdigo de forma jerrquica, empezando por el main: #include <QApplication> #include "ventana.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Ventana ventana; ventana.show(); return app.exec(); } Lo primero que llama la atencin es que no usamos ni QWidget, ni QLabel ni nada, slo Ventana. Es una forma de aislar completamente la ejecucin y tener bien localizados todos los componentes. He aqu el archivo ventana.h: #ifndef VENTANA_H #define VENTANA_H #include <QWidget>
F.J. Melero c

1.5 Qt y OpenGL #include "visoropengl.h" class Ventana : public QWidget { Q_OBJECT public: Ventana(); private: VisorOpenGL *visorOpenGL; }; #endif

15

Podemos ver como la Ventana no es ms que un QWidget modicado. En el constructor realizamos todas las operaciones de creacin de componentes y organizacin de los mismos: #include <QtGui> #include "visoropengl.h" #include "ventana.h" Ventana::Ventana() { visorOpenGL = new VisorOpenGL; QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->addWidget(visorOpenGL); setLayout(mainLayout); setVentanaTitle(tr("Informtica Grfica. Pepito Prez Prez")); }

1.5.1.

Denicin de VisorOpenGL

La clase VisorOpenGL contiene algunas deniciones pblicas clsicas, como son el constructor, el destructor y los mtodos sizeHint(), y minimumSizeHint(): #ifndef VISOROPENGL #define VISOROPENGL #include #include #include #include <QGLWidget> <QEvent> <QMouseEvent> "escena.h"

class VisorOpenGL: public QGLWidget { Q_OBJECT public: VisorOpenGL(QWidget *parent = 0);


F.J. Melero c

16 ~VisorOpenGL(); QSize minimumSizeHint() const; QSize sizeHint() const; public slots: void setXRotation(float angle); void setYRotation(float angle); void setZRotation(int angle); signals: void xRotationChanged(float angle); void yRotationChanged(float angle); void zRotationChanged(int angle); protected: void initializeGL(); void paintGL(); void resizeGL(int width, int height); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void keyPressEvent(QKeyEvent *event); void setObserver(); void setProjection(); void dibujarEscena(); private: QPoint lastPos; Escena escena; GLint Draw_type; int int int int Window_width; Window_height; Front_plane; Back_plane; Observer_distance; observer_angle_x; observer_angle_y; observer_angle_z;

Introduccin al uso de Qt

float float float float

void normalizeAngle(float angle); }; #endif Usamos un destructor para asegurarnos que se libera la memoria que no vayamos a usar.
F.J. Melero c

1.5 Qt y OpenGL

17

Las seales y slots se usan para permitir a otros objetos interactuar con la escena 3D. La inicializaciin de OpenGL, la redimensin del viewport, y el dibujado de la escena se realizan reimplementando las funciones QGLWidget::initializeGL(), QGLWidget::resizeGL(), y QGLWidget::paintGL(). Para permitir al usuario interactuar directamente con la escena usando el ratn, reimplementamos QWidget::mousePressEvent() y QWidget::mouseMoveEvent(), y para interactuar con el teclado reimplementamos QWidget::keyPressEvent(). El resto de la clase contiene funciones y variables tiles para construir y manipular la escena.

1.5.2.

Implementacin de VisorOpenGL

En este ejemplo dividiremos la clase en grupos de funciones, y las describiremos por separado. De esta forma se ver la diferencia entre sublclases de componentes nativos (como QWidget y QFrame) y subclases de QGLWidget. Construccin del componente y redimensin El constructor proporciona unos ngulos de rotacin por defecto, y puede ser utilizado para denir algunos colores. En nuestro caso, sinicializaremos los ngulos de rotacin, la dimensin del volumen de visualizacin y la posicin del observador. VisorOpenGL::VisorOpenGL(QWidget *parent) : QGLWidget(parent) { // Inicializamos los ngulos de rotacin observer_angle_x = 0; observer_angle_y = 0; observer_angle_z = 0; // Definimos el tamao del volumen de visualizacin Window_width=5; Window_height=5; Front_plane=10; Back_plane=1000; // se inicia la posicion del observador, en el eje z Observer_distance=2*Front_plane; // permitimos al componente tener el foco y aceptar eventos setFocusPolicy(Qt::StrongFocus); } Tambin implementamos un destructor, para liberar los recursos de OpenGL en el momento de la eliminacin del componente.: VisorOpenGL::~VisorOpenGL() { makeCurrent(); }
F.J. Melero c

18

Introduccin al uso de Qt

Las dos siguientes funciones sirven para controlar de alguna forma que el componente se visualiza con unas dimensiones adecuadas. Denimos las dimensiones mnimas y las ptimas. QSize VisorOpenGL::minimumSizeHint() const { return QSize(50, 50); } QSize VisorOpenGL::sizeHint() const { return QSize(400, 400); } El visor proporciona tres slots que permiten a otros componentes cambiar la orientacin de la escena: void VisorOpenGL::setXRotation(int angle) { normalizeAngle(&angle); if (angle != xRot) { xRot = angle; emit xRotationChanged(angle); updateGL(); } } Ntese que la variable xRot slo se actualiza si el ngulo nuevo es diferente al antiguo. Entonces, se emite la seal xRotationChanged() de forma que otros componentes puedan actualizarse y se llama al mtodo updateGL(), ya existente por defecto en el componente. Los mtodos setYRotation() y setZRotation() realizan la tarea anloga para los otros ejes.

1.5.3.

Inicializacin de OpenGL

Independientemente del sistema gestor de ventanas y de interfaz de usuario que utilicemos, es necesario realizar una tarea de inicializacin del motor de OpenGL, previamente a cualquier operacin de dibujado. Normalmente estas tareas estn relacionadas con la denicin de colores, materiales, habilitacin de caracteristicas (doble buffer, zbuffer, etc...) y otras propiedades del proceso de dibujado. void VisorOpenGL::initializeGL() { glClearColor(1.0,1.0,1.0,1.0); glShadeModel(GL_FLAT); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } Podemos ver como se ha denido un color de fondo (blanco), y se ha denido que el sombreado sea plano. Adems, se habilita el test de profundidad y la deteccin de caras ocultas.
F.J. Melero c

1.5 Qt y OpenGL

19

1.5.4.

Redimensin del Viewport

La funcin resizeGL() se usa para asegurar que OpenGL dibuja la escena en un viewport que coincida con las dimensiones en pxeles del componente, realizando las transformaciones de vista necesarias para pasar de 3D a 2D. Esta funcin es llamada siempre que cambian las dimensiones del componente, y se le pasa como parmetro la nueva anchura y altura. Lo que hacemos es denir un viewport cuadrado con lado igual a la dimensin ms pequea del componente, de forma que la escena no se vea distorsionada si el componente adquiere forma rectangular. A continuacin, se llama a setProjection() que establece las dimensiones del volumen de visualizacin. void VisorOpenGL::resizeGL(int width, int height) { int side = qMin(width, height); glViewport((width - side) / 2, (height - side) / 2, side, side); setProjection(); } void VisorOpenGL::setProjection(){ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-Window_width,Window_width, -Window_height,Window_height, Front_plane,Back_plane); glMatrixMode(GL_MODELVIEW); }

1.5.5.

Dibujando la escena

La funcin paintGL() se usa para pintar los contenidos de la escena en el componente. En nuestro caso, como slo vamos a hacer uso de OpenGL para dibujar, reimplementamos la funcin QGLWidget::paintGL() en lugar de la bsica QWidget::paintEvent(): void VisorOpenGL::setObserver(){ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0,0,-Observer_distance); glRotatef(observer_angle_x,1,0,0); glRotatef(observer_angle_y,0,1,0); } void VisorOpenGL::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); setProjection(); setObserver(); dibujarEscena();
F.J. Melero c

20 }

Introduccin al uso de Qt

Podemos ver como lo primero que hacemos es borrar el color de fondo (denido en initializeGL() ) y limpiamos el zbuffer. A continuacin, invocamos la funcin setObserver() que se encarga de aplicar las transformaciones necesarias para que la escena sea visible desde donde deseamos. Finalmente, llamamos a un mtodo que se encarga de dibujar la escena, invocando al mtodo dibujarEscena(). En principio se podra incluir el cdigo de dibujado en esta misma funcin, pero lo hemos separado para que queden ms claras las distintas etapas que intervienen en el dibujado de una escena OpenGL: void VisorOpenGL::dibujarEscena() { this->escena.draw(); } En nuestro objeto VisorOpenGL tenemos un elemento Escena, que es el que se encargar de organizar todos los elementos a dibujar. La Escena La escena, en el ejemplo que proporcionamos, se basa en unos ejes de coordenadas (encapsulados en la clase Axis) y ocho puntos que sern conectados de forma distinta segn las primitivas grcas que decidamos. Para la mayora de la prcticas de la asignatura, sta es la nica clase de las proporcionadas en el ejemplo que tendremos que modicar (ello no quita que habr que aadir ms). La denicin de la escena (escena.h) es: #ifndef ESCENA_H #define ESCENA_H #include "axis.h" class Escena { private: Axis ejes; GLuint Draw_type; GLfloat vertices[8][3]; public: Escena(); void setDrawType(GLuint drawtype); void draw(); }; #endif De toda la clase, aparte del constructor que se puede consultar en el cdigo proporcionado, lo ms interesante es la funcin de dibujado. Ntese como no hay ningn tipo de cdigo referente al observador o a las transformaciones de vista, sino simplemente el dibujado de los elementos que queremos que aparezcan:
F.J. Melero c

1.5 Qt y OpenGL

21

Figura 1.4: Puntos dibujados en modo GL_LINE_LOOP

void Escena::draw(){ ejes.draw(); glColor3f(0.0,0.0,0.0); glPointSize(4); switch (Draw_type){ case GL_POINTS: glBegin(GL_POINTS); break; case GL_LINES: glBegin(GL_LINES); break; case GL_LINE_STRIP: glBegin(GL_LINE_STRIP); break; case GL_LINE_LOOP: glBegin(GL_LINE_LOOP); break; case GL_POLYGON: glBegin(GL_POLYGON); break; case GL_QUADS: glBegin(GL_QUADS); break; case GL_QUAD_STRIP: glBegin(GL_QUAD_STRIP); break; case GL_TRIANGLES: glBegin(GL_TRIANGLES); break; case GL_TRIANGLE_STRIP: glBegin(GL_TRIANGLE_STRIP); break; case GL_TRIANGLE_FAN: glBegin(GL_TRIANGLE_FAN); glVertex3f(0,0,0); break; } for (int i=0;i<8;i++){ glVertex3fv((GLfloat *) &vertices[i]); } glEnd(); }

F.J. Melero c

22

Introduccin al uso de Qt

1.5.6.

Gestin del ratn

Tal y como se hace con las subclases de los componentes nativos, los eventos de ratn se gestionan reimplementando funciones tales como QWidget::mousePressEvent() y QWidget::mouseMoveEvent(). La funcin mousePressEvent(), en nuestro caso, se va a encargar slo de almacenar la posicin del cursor del ratn en el momento en que hacemos clic. void VisorOpenGL::mousePressEvent(QMouseEvent *event) { lastPos = event->pos(); } La funcin mouseMoveEvent() usa la localizacin anterior del cursor para determinar cunto debe rotarse el objeto, y en qu direccin. Por ahora slo vamos a permitir la rotacin en los ejes X e Y. void VisorOpenGL::mouseMoveEvent(QMouseEvent *event) int dx = event->x() - lastPos.x(); int dy = event->y() - lastPos.y(); if (event->buttons() & Qt::LeftButton) { setXRotation(xRot + 8 * dy); setYRotation(yRot + 8 * dx); } lastPos = event->pos(); } {

1.5.7.

Gestin del teclado

La gestin de los eventos de teclado se realiza de forma anloga a la de los eventos de ratn, en este caso reimplementando el mtodo keyPressEvent(): void VisorOpenGL::keyPressEvent(QKeyEvent *event){ switch( event->key() ) case Qt::Key_Right: { setYRotation(++observer_angle_y); break; case Qt::Key_Left: setYRotation(--observer_angle_y); break; case Qt::Key_Up: setXRotation(--observer_angle_x); break; case Qt::Key_Down: setXRotation(++observer_angle_x); break; case Qt::Key_PageUp: Observer_distance*=1.2;break; case Qt::Key_PageDown: Observer_distance/=1.2;break; // Primitivas Grficas case Qt::Key_F1: Draw_type=GL_POINTS;break; case Qt::Key_F2: Draw_type=GL_LINES;break; case Qt::Key_F3: Draw_type=GL_LINE_STRIP;break;

F.J. Melero c

1.5 Qt y OpenGL case Qt::Key_F4: Draw_type=GL_LINE_LOOP;break; case Qt::Key_F5: Draw_type=GL_POLYGON;break; case Qt::Key_F6: Draw_type=GL_QUADS;break; case Qt::Key_F7: Draw_type=GL_QUAD_STRIP;break; case Qt::Key_F8: Draw_type=GL_TRIANGLES;break; case Qt::Key_F9: Draw_type=GL_TRIANGLE_STRIP;break; case Qt::Key_F10:Draw_type=GL_TRIANGLE_FAN;break; case Qt::Key_Q: QCoreApplication::exit(0); default: QGLWidget::keyPressEvent(event); } this->escena.setDrawType(Draw_type); updateGL(); }

23

Se puede observar que es un mecanismo muy sencillo, ya que la conexin entre la seal keyPress y el slot keyPressEvent estn predenidas por defecto en QGLWidget. En este caso, denimos un incremento o decremento de los ngulos de rotacin en X o en Y usando los cursores, el avance o retroceso del observador con AvPag o RePag, y el cambio de modo de dibujado de las primitivas grcas que se solicita en la prctica 1.

1.5.8.

Resumen

Hemos visto cmo realizar una especializacin de la clase QGLWidget para tener un visor donde poder pintar escenas con OpenGL. Al ser QGLWidget una subclase de QWidget, todas sus derivadas se puecen colocar en paneles y se puede interactuar con ellas como con cualquier otro componente. Para asegurarnos de que el visor pinta correctamente la escena, hay que reimplementar al menos las siguientes funciones: QGLWidget::initializeGL(), que inicializa los recursos de OpenGL. QGLWidget::resizeGL(), que controla las variaciones de tamao del visor y ajusta el viewport a las dimensiones adecuadas, calculando la nueva transformacin de vista. QGLWidget::paintGL(), que se encarga de realizar las llamadas OpenGL para su visualizacin. Importante: En el archivo de proyecto .pro hay que aadir la siguiente lnea para que compile con las cabeceras y libreras especcas para OpenGL: QT += opengl

F.J. Melero c

24

Introduccin al uso de Qt

F.J. Melero c

Bibliografa
[1] Qt Reference Documentation (Open Source Edition), http://doc.trolltech.com/4.3/ [2] Completo tutorial online de OpenGL con Qt. http://www.digitalfanatics.org/projects/qt_tutorial /chapter14.html [3] Web ocial del OpenGL consortium http://www.opengl.org [4] J. Blanchette y M. Summereld, C++ GUI Programming with Qt 4, 2nd Edition, Prentice Hall, 2008 (Versin online desde la biblioteca en Safari: http://safari.oreilly.com/9780137143979 )

F.J. Melero c

Vous aimerez peut-être aussi