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 3
1. Introduccin al uso de Qt 5
1.1. Qu es Qt? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.1. Cmo est disponible Qt? . . . . . . . . . . . . . . . . . . . . . . 6
1.1.2. Cmo obtener e instalarse Qt? . . . . . . . . . . . . . . . . . . . 6
1.2. Hola Mundo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.1. Lnea a lnea . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.2. Compilacin y ejecucin . . . . . . . . . . . . . . . . . . . . . . . 9
1.3. Algo ms complejo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
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. Denicin 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
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
6 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 desarrolla-
dores 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 solucio-
nes 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 dispo-
nible 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 Sour-
ce 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-
.
a
gradecer.
ec
onmicamente 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 instruc-
ciones indicadas en el archivo (ftp://ftp.trolltech.com/qt/source/INSTALL).
Podemos tanto descargarnos los fuentes y compilar e instalar las libreras, como bajar-
nos uno de los paquetes precompilados preparados especcamente para nuestra distribu-
cin 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 7
% 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
8 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() aca-
bar 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 9
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 make-
le 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 ven-
tana con un botn que, al pulsarlo, nalizar la ejecucin:
#include <QApplication>
#include <QFont>
#include <QPushButton>
#include <QLabel>
#include <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 compo-
nente 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 esqui-
na 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 comu-
nicar 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 ca-
llback 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 aco-
plamiento 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 norma-
les. 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 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 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); // a.value() == 12, b.value() == 12
b.setValue(48); // 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 pue-
den 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 asig-
natura. 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 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 modicado. En el cons-
tructor 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 <QGLWidget>
#include <QEvent>
#include <QMouseEvent>
#include "escena.h"
class VisorOpenGL: public QGLWidget {
Q_OBJECT
public:
VisorOpenGL(QWidget
*
parent = 0);
F.J. Melero c
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 Window_width;
int Window_height;
int Front_plane;
int Back_plane;
float Observer_distance;
float observer_angle_x;
float observer_angle_y;
float observer_angle_z;
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 in-
teractuar 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 mo-
mento 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 pa-
ra los otros ejes.
1.5.3. Inicializacin de OpenGL
Independientemente del sistema gestor de ventanas y de interfaz de usuario que utilice-
mos, es necesario realizar una tarea de inicializacin del motor de OpenGL, previamente a
cualquier operacin de dibujado. Normalmente estas tareas estn relacionadas con la de-
nicin 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 trans-
formaciones 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 componen-
te. En nuestro caso, como slo vamos a hacer uso de OpenGL para dibujar, reimplementa-
mos 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 proporciona-
do, 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 almace-
nar 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 determi-
nar 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 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 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

También podría gustarte