Está en la página 1de 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

1.4. Gestin de eventos en Qt . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

1.4.1. Seales y slots . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

1.4.2. Un ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12

1.5. Qt y OpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

1.5.1. Definicin de VisorOpenGL . . . . . . . . . . . . . . . . . . . . .

15

1.5.2. Implementacin de VisorOpenGL . . . . . . . . . . . . . . . . . .

17

1.5.3. Inicializacin de OpenGL . . . . . . . . . . . . . . . . . . . . . .

18

1.5.4. Redimensin del Viewport . . . . . . . . . . . . . . . . . . . . . .

19

1.5.5. Dibujando la escena . . . . . . . . . . . . . . . . . . . . . . . . .

19

1.5.6. Gestin del ratn . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

1.5.7. Gestin del teclado . . . . . . . . . . . . . . . . . . . . . . . . . .

22

1.5.8. Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

23

c
F.J. Melero

Captulo 1

Introduccin al uso de Qt
1.1.

Qu es Qt?

Es una librera para desarrollar aplicaciones con interfaz grfico 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 grficamente (echadle un vistazo, es
bastante bueno),
Qt Linguist, una herramienta para crear aplicaciones multilinges
Qt Assistant, un generador de documentacin
qmake, un generador de Makefiles 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


c
F.J. Melero

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 definitiva,
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 definitiva, Qt no exige dinero salvo que el desarrollador obtenga beneficio 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 especficamente 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:
c
F.J. Melero

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 -prefix la ruta deseada: sudo
-c "make install -prefix /ruta/deseada"
3. Configuracin 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();
}
c
F.J. Melero

Introduccin al uso de Qt

1.2.1.

Lnea a lnea

#include <QApplication>
Incluimos la definicin de la clase QApplication. Debe haber uno y slo un objeto
QApplication en cada programa que haga uso de un interfaz grfico con Qt. La clase
QApplication gestiona temas tales como el cursor del ratn, la fuente por defecto, etc...
#include <QLabel>
Incluimos la definicin de la clase QLabel. Todas las clases de Qt comienzan con la letra
Q, y para cada clase de la API, hay un fichero .h con el mismo nombre que contiene su
definicin. 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 modificar 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 finalice 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.
c
F.J. Melero

1.3 Algo ms complejo

1.2.2.

Compilacin y ejecucin

Para compilar es necesario tener un makefile. 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 makefile 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, finalizar 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();
}

c
F.J. Melero

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 grfica... Adems,
un componente es recortado por su padre y por los que se superponen a l.
Si no se especifica 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);
Definimos 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);
c
F.J. Melero

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 predefinidos en los objetos en cuestin, lo cual no
impide que nosotros definamos 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 grfico 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 predefinidas, pero podemos crear componentes derivados y aadir
c
F.J. Melero

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 predefinidos, 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 notifica
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:
c
F.J. Melero

1.4 Gestin de eventos en Qt

13

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;
};
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 definan 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;
c
F.J. Melero

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 infinitos 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>
c
F.J. Melero

1.5 Qt y OpenGL

15

#include "visoropengl.h"
class Ventana : public QWidget {
Q_OBJECT
public:
Ventana();
private:
VisorOpenGL *visorOpenGL;
};
#endif
Podemos ver como la Ventana no es ms que un QWidget modificado. 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.

Definicin de VisorOpenGL

La clase VisorOpenGL contiene algunas definiciones 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);
c
F.J. Melero

16

Introduccin al uso de Qt
~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;

float
float
float
float

Observer_distance;
observer_angle_x;
observer_angle_y;
observer_angle_z;

void normalizeAngle(float angle);


};
#endif
Usamos un destructor para asegurarnos que se libera la memoria que no vayamos a usar.
c
F.J. Melero

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 definir 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();
}
c
F.J. Melero

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. Definimos 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 definicin 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 definido un color de fondo (blanco), y se ha definido que el
sombreado sea plano. Adems, se habilita el test de profundidad y la deteccin de caras
ocultas.
c
F.J. Melero

1.5 Qt y OpenGL

1.5.4.

19

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 definir 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();
c
F.J. Melero

20

Introduccin al uso de Qt

}
Podemos ver como lo primero que hacemos es borrar el color de fondo (definido 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 grficas 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 modificar (ello
no quita que habr que aadir ms).
La definicin 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:
c
F.J. Melero

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();
}

c
F.J. Melero

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;

c
F.J. Melero

1.5 Qt y OpenGL

23

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();
}
Se puede observar que es un mecanismo muy sencillo, ya que la conexin entre la seal
keyPress y el slot keyPressEvent estn predefinidas por defecto en QGLWidget.
En este caso, definimos 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 grficas 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 especficas para OpenGL:
QT += opengl

c
F.J. Melero

24

c
F.J. Melero

Introduccin al uso de Qt

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 oficial del OpenGL consortium http://www.opengl.org
[4] J. Blanchette y M. Summerfield, C++ GUI Programming with Qt 4, 2nd
Edition, Prentice Hall, 2008 (Versin online desde la biblioteca en Safari:
http://safari.oreilly.com/9780137143979 )

c
F.J. Melero

También podría gustarte