0% encontró este documento útil (0 votos)
116 vistas

Trans QT

Cargado por

Camilo Rivera
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
116 vistas

Trans QT

Cargado por

Camilo Rivera
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 76

Nuevas Tecnologas de la

Programaci on
M odulo 2: Programaci on graca en entorno UNIX con una
librera de alto nivel (Qt)
Andres Cano Utrera
Dpto. Ciencias de la Computaci on e I.A
Universidad de Granada
Octubre de 2011

Indice
1. Introduccion a la programacion con Qt 1
2. Primeros programas con Qt 2
2.1. Primer programa con Qt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2. Compilaci on del programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.3. Conexi on de acciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.4. Gestores de posicionamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3. Creacion de dialogos 8
3.1. Ejemplo de creacion de una subclase de Dialogo . . . . . . . . . . . . . . . . . 10
3.2. M as sobre se nales y slots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4. Un vistazo a los widgets de Qt 18
4.1. Botones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.2. Contenedores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.3. Visores de items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.4. Widgets para mostrar informacion . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.5. Widgets de entrada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.6. Di alogos predenidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5. La ventana principal 28
5.1. Denici on de la clase ventana principal . . . . . . . . . . . . . . . . . . . . . . 28
5.2. Constructor de la clase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
5.3. El icono de la aplicacion y mecanismo de recursos . . . . . . . . . . . . . . . . 31
5.4. Creaci on de men us y toolbars . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
5.4.1. Creaci on de las acciones . . . . . . . . . . . . . . . . . . . . . . . . . . 33
5.4.2. Creaci on de los men us . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
0-0

INDICE 0-1
5.4.3. Creaci on de men us contextuales . . . . . . . . . . . . . . . . . . . . . . 35
5.4.4. Creaci on de barras de utilidades . . . . . . . . . . . . . . . . . . . . . . 35
5.5. Creaci on de la barra de estado . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.6. Implementaci on de los slots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.7. Introducci on al manejo de eventos de bajo nivel . . . . . . . . . . . . . . . . . 40
5.8. Uso de nuestros dialogos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.9. Almacenamiento de la conguracion de la aplicaci on . . . . . . . . . . . . . . . 43
5.10. Implementacion del widget central . . . . . . . . . . . . . . . . . . . . . . . . . 44
6. Sistema de dibujo de Qt 45
6.1. Dibujar con QPainter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.2. Otros atributos del QPainter . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
6.3. Transformaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
6.4. Modos de composici on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.5. Rendering de alta calidad con QImage . . . . . . . . . . . . . . . . . . . . . . . 54
6.6. Clases QImage y QPixmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
6.6.1. Clase QImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
6.6.2. Clase QPixmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
6.6.3. Clase QPicture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
7. Creacion de widgets optimizados 61
7.1. Ejemplo: Un spin box hexadecimal . . . . . . . . . . . . . . . . . . . . . . . . . 61
7.2. Subclases de QWidget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
7.3. Ejemplo: Un editor de iconos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
7.3.1. El chero .h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
7.3.2. El chero .cpp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
1 INTRODUCCI

ON A LA PROGRAMACI

ON CON QT 1
1. Introducci on a la programacion con Qt
Qt es un toolkit de plataforma cruzada (cross-platform) que empezo a
desarrollarse por Trolltech (empresa Noruega) en 1992 para construir
GUIs en C++. En junio de 2008 Nokia compro a Tolltech.
Las primeras versiones datan de 1996.
Puede funcionar en varias plataformas: Microsoft Windows, Unix/X11,
Mac OS X.
Con el se han desarrollado aplicaciones tales como: Autodesk, Google
Earth, Adobe Photoshop Elements, Skype, KDE, Avidemux, LyX,
Mathematica (version linux), doxygen, VirtualBox, etc.
Se distribuye bajo ediciones comerciales o bien Open Source.
Incluye una extensa librera de clases C++ y utilidades para construir
las aplicaciones de forma rapida y sencilla.
2 PRIMEROS PROGRAMAS CON QT 2
2. Primeros programas con Qt
2.1. Primer programa con Qt
Hello Qt: chap01/hello.cpp
1 #include <QApplication>
2 #include <QLabel>
3 int main(int argc, char
*
argv[])
4 {
5 QApplication app(argc, argv);
6 QLabel
*
label = new QLabel("Hello Qt!");
7 label->show();
8 return app.exec();
9 }
Las lneas 1 y 2 incluyen las deniciones de las clases QApplication y
QLabel.
Para cada clase Qt existe un chero de cabecera con el mismo
nombre
La lnea 5 crea un objeto QApplication
El constructor necesita argc y argv pues Qt soporta algunos
argumentos propios en la lnea de comandos.
La lnea 6 crea un widget (window gadget) (control o componente y
contenedor al mismo tiempo) QLabel con la cadena Hello Qt!
Los widgets pueden contener otros widgets.
Normalmente las aplicaciones usan QMainWindow o QDialog como
ventana de la aplicacion.
Por ejemplo, la ventana de la aplicacion contiene normalmente un
QMenuBar, QToolBars, una QStatusBar y otros widgets.
Pero cualquier widget puede actuar como ventana como ocurre en el
ejemplo con QLabel.
2 PRIMEROS PROGRAMAS CON QT 3
La lnea 7 muestra la etiqueta en pantalla.
Los widgets se crean siempre ocultos, para que puedan
optimizarse antes de mostrarlos (evitando el parpadeo).
La lnea 8 da el control de la aplicacion a Qt, comenzando el bucle de
eventos.
2.2. Compilaci on del programa
El codigo lo incluimos en un chero hello.cpp dentro del directorio
hello.
Dentro del directorio hello ejecutamos para crear el chero de
proyecto (hello.pro):
qmake-q4 -project
Para crear el chero Makefile:
qmake-q4 hello.pro
Para obtener el ejecutable:
make
2 PRIMEROS PROGRAMAS CON QT 4
2.3. Conexion de acciones
Ejemplo de control de eventos: chap01/quit.cpp
1 #include <QApplication>
2 #include <QPushButton>
3 int main(int argc, char
*
argv[])
4 {
5 QApplication app(argc, argv);
6 QPushButton
*
button = new QPushButton("Quit");
7 QObject::connect(button, SIGNAL(clicked()),
8 &app, SLOT(quit()));
9 button->show();
10 return app.exec();
11 }
Los widgets emiten se nales (signals) para indicar que se ha producido
una accion de usuario o cambio de estado en el widget.
Las se nales pueden conectarse a una funcion (slot) que se
ejecutara automaticamente cuando se emita la se nal.
En la lnea 7 se conecta la se nal clicked() del boton al slot quit()
del objeto QApplication.
2.4. Gestores de posicionamiento
Permiten controlar automaticamente la geometra (posicion y tama no)
de los widgets que contiene.
QHBoxLayout: Coloca los widgets horizontalmente de izquierda a
derecha.
2 PRIMEROS PROGRAMAS CON QT 5
QVBoxLayout: Coloca los widgets verticalmente de arriba hacia
abajo.
QGridLayout: Coloca los widgets en una rejilla.
QFormLayout: Coloca los widgets en dos columnas.
2 PRIMEROS PROGRAMAS CON QT 6
El siguiente programa usa tres widgets: QSpinBox, QSlider y QWidget
(ventana principal).
Los dos primeros seran hijos del tercero.
Ejemplo de Layouts y control de eventos: chap01/age.cpp
1 #include <QApplication>
2 #include <QHBoxLayout>
3 #include <QSlider>
4 #include <QSpinBox>
5 int main(int argc, char
*
argv[])
6 {
7 QApplication app(argc, argv);
8 QWidget
*
window = new QWidget;
9 window->setWindowTitle("Enter Your Age");
10 QSpinBox
*
spinBox = new QSpinBox;
11 QSlider
*
slider = new QSlider(Qt::Horizontal);
12 spinBox->setRange(0, 130);
13 slider->setRange(0, 130);
14 QObject::connect(spinBox, SIGNAL(valueChanged(int)),
15 slider, SLOT(setValue(int)));
16 QObject::connect(slider, SIGNAL(valueChanged(int)),
17 spinBox, SLOT(setValue(int)));
18 spinBox->setValue(35);
19 QHBoxLayout
*
layout = new QHBoxLayout;
20 layout->addWidget(spinBox);
21 layout->addWidget(slider);
22 window->setLayout(layout);
23 window->show();
24 return app.exec();
25 }
Las lneas 8 a 11 crean los tres widgets.
Al crear un widget debe pasarse el padre en el constructor,
aunque en el ejemplo no hace falta hacerlo por lo que veremos
mas adelante.
2 PRIMEROS PROGRAMAS CON QT 7
Las lneas 12 y 13 establecen los rangos validos para el QSpinBox y
QSlider.
Las lneas 14 a 17 hacen que los objetos QSpinBox y QSlider esten
siempre sincronizados mostrando el mismo valor.
Las lneas 19 a 22 colocan los widgets QSpinBox y QSlider usando un
gestor de posicionamiento (layout manager)
La lnea 22, a parte de instalar el gestor de posicionamiento en la
ventana, hace que los widgets QSpinBox y QSlider se hagan hijos del
widget de la ventana (window)
3 CREACI

ON DE DI

ALOGOS 8
3. Creaci on de dialogos
La mayora de las aplicaciones contienen una ventana principal con una
barra de men us, una barra de herramientas, y varios dialogos.
Los dialogos muestran al usuario una serie de opciones que el usuario
puede modicar de valor.
Qt dispone de una serie de dialogos predenidos.
Tambien permite crear nuestros propios dialogos.
En Qt podemos crear tanto dialogos modales como no modales con una
clase que herede de QDialog:
Dialogos modales: Bloquean el resto del interfaz graco hasta que
el usuario los cierra.
Se muestran con el metodo exec() (que devuelve un entero que
indica que el dialogo se ha cerrado).
exec() devuelve habitualmente los valores QDialog:Accepted o
QDialog:Rejected (correspondientes a la pulsacion de los
botones OK o Cancel).
Normalmente se conectan los botones del dialogo a los slots
QDialog:accept(), QDialog:reject() o QDialog:done(int)
para que exec() devuelva el valor correspondiente.
Ejemplo de dialogo modal: MisSources/modal/modal.cpp
1 class MyDialog : public QDialog
2 {
3 Q_OBJECT
4 public:
5 MyDialog();
6 bool isCheckBoxChecked() const {
7 return _checkbox->isChecked(); }
8 private:
9 QCheckBox
*
_checkbox;
10 };
3 CREACI

ON DE DI

ALOGOS 9
11 MyDialog::MyDialog() :
12 QDialog( 0)
13 {
14 _checkbox = new QCheckBox( "Check me", this );
15 QPushButton
*
okbutton = new QPushButton( "OK", this );
16 QObject::connect( okbutton, SIGNAL( clicked() ),
17 this, SLOT( accept() ) );
18 QPushButton
*
cancelbutton = new QPushButton( "Cancel",
19 this );
20 QObject::connect( cancelbutton, SIGNAL( clicked() ),
21 this, SLOT( reject() ) );
22 }
23
24 class ClickReceiver : public QObject
25 {
26 Q_OBJECT
27 public slots:
28 void slotShowDialog();
29 };
30 void ClickReceiver::slotShowDialog()
31 {
32 MyDialog
*
dialog = new MyDialog();
33 int ret = dialog->exec();
34 if( ret == QDialog::Accepted )
35 {
36 qDebug( "User pressed OK; check box was %s checked.\n ",
37 (dialog->isCheckBoxChecked() ? "" : "not") );
38 }
39 else
40 {
41 qDebug( "User pressed Cancel\n" );
42 }
43 }
Dialogos no modales: No bloquean el resto del interfaz graco.
3 CREACI

ON DE DI

ALOGOS 10
3.1. Ejemplo de creacion de una subclase de Dialogo
Crearemos una subclase independiente por s sola, con sus propias
se nales y slots.
El codigo fuente lo dividimos en dos cheros: finddialog.h y
finddialog.cpp.
Ejemplo de dialogo: chap02/nd/nddialog.h
1 #ifndef FINDDIALOG_H
2 #define FINDDIALOG_H
3 #include <QDialog>
4 class QCheckBox;
5 class QLabel;
6 class QLineEdit;
7 class QPushButton;
Las lneas 1, 2 y 27 impiden que el chero de cabecera pueda ser
incluido varias veces.
La lnea 3 incluye la clase base para los dialogos Qt: QDialog (que es
subclase de QWidget).
Las lneas 4 a 7 son declaraciones adelantadas (forward
declarations) de las clases que se usaran mas adelante.
3 CREACI

ON DE DI

ALOGOS 11
8 class FindDialog : public QDialog
9 {
10 Q_OBJECT
11 public:
12 FindDialog(QWidget
*
parent = 0);
La lnea 8 dene la clase FindDialog como subclase de QDialog:
En la lnea 10 se incluye la macro Q_OBJECT, que es necesaria en todas
las clases que denen sus propias se nales o slots.
Esto hara que el makele generado por qmake incluya reglas
especiales para ejecutar moc (el compilador de meta-objetos)
Para que moc funcione correctamente, es necesario que pongamos la
denicion de la clase en un chero .h separado del chero de
implementacion.
moc genera codigo C++ que incluye este chero .h.
La denicion del constructor de FindDialog es tpico en clases de
widgets Qt: parent especica el widget padre.
parent es por defecto un puntero nulo, que signica que el dialogo no
tiene ning un padre.
13 signals:
14 void findNext(const QString &str, Qt::CaseSensitivity cs);
15 void findPrevious(const QString &str, Qt::CaseSensitivity cs);
La seccion signals de las lneas 13 a 15 declara dos se nales que el
dialogo emite cuando el usuario pincha el boton Find.
Si esta seleccionada la opcion Search backward, se emite
findPrevious()
En caso contrario se emite findNext()
La palabra signals es una macro que el preprocesador de C++
convertira en codigo C++.
Qt::CaseSensitivity es un tipo enumerado (enum) que puede tomar
los valores Qt::CaseSensitive y Qt::CaseInsensitive
3 CREACI

ON DE DI

ALOGOS 12
16 private slots:
17 void findClicked();
18 void enableFindButton(const QString &text);
19 private:
20 QLabel
*
label;
21 QLineEdit
*
lineEdit;
22 QCheckBox
*
caseCheckBox;
23 QCheckBox
*
backwardCheckBox;
24 QPushButton
*
findButton;
25 QPushButton
*
closeButton;
26 };
27 #endif
En las lneas 16 a 18 se declaran dos slots (en la seccion privada de la
clase). La palabra slots es tambien una macro.
En las lneas 19 a 25 se declaran punteros a los widgets hijos del
dialogo para usarlos luego en los slots.
Ejemplo de dialogo: chap02/nd/nddialog.cpp
1 #include <QtGui>
2 #include "finddialog.h"
Qt contiene varios modulos, cada uno en su propia librera: QtCore,
QtGui, QtNetwork, QtOpenGL, QtSql, QtSvg, QtXml, etc.
El chero de cabecera <QtGui> contiene la denicion de todas las clases
de QtCore y QtGui. Su inclusion evita tener que incluir cada clase
individualmente.
3 CREACI

ON DE DI

ALOGOS 13
3 FindDialog::FindDialog(QWidget
*
parent)
4 : QDialog(parent)
5 {
6 label = new QLabel(tr("Find &what:"));
7 lineEdit = new QLineEdit;
8 label->setBuddy(lineEdit);
9 caseCheckBox = new QCheckBox(tr("Match &case"));
10 backwardCheckBox = new QCheckBox(tr("Search &backward"));
11 findButton = new QPushButton(tr("&Find"));
12 findButton->setDefault(true);
13 findButton->setEnabled(false);
14 closeButton = new QPushButton(tr("Close"));
En la lnea 4 se pasa el parametro parent al constructor de la clase
base.
La funcion tr(cadena) marca la cadena para ser traducida al lenguaje
nativo. (esta declarada en la clase QObject y todas las clases que
contengan la macro Q_OBJECT)
En la cadena usada al crear algunos objetos (lneas 6, 9, 10, 11) se usa
el caracter & para denir una tecla aceleradora: Alt+tecla.
La lnea 8 hace que lineEdit sea el amigote (buddy) de label: al
pulsar Alt+W el foco del teclado lo obtiene lineEdit.
En la lnea 12, el boton Find se hace el bot on por defecto (es aquel que
se ejecuta al pulsar la tecla Enter).
La lnea 13 desactiva el boton Find (aparece en grisaceo y no responde
a los clicks)
3 CREACI

ON DE DI

ALOGOS 14
15 connect(lineEdit, SIGNAL(textChanged(const QString &)),
16 this, SLOT(enableFindButton(const QString &)));
17 connect(findButton, SIGNAL(clicked()),
18 this, SLOT(findClicked()));
19 connect(closeButton, SIGNAL(clicked()),
20 this, SLOT(close()));
Las lneas 15 a 20 conectan se nales con slots:
Fijarse que no es necesario poner QObject:: delante de las llamadas
a connect() ya que QObject es una clase ancestral de FindDialog.
El slot enableFindButton(const QString &) se llama cuando
cambia el texto de lineEdit.
El slot findClicked(const QString &) se llama cuando el usuario
pincha el boton Find.
El slot close() (heredado de QWidget, oculta el widget sin
borrarlo) se llama cuando el usuario pincha el boton Close.
3 CREACI

ON DE DI

ALOGOS 15
21 QHBoxLayout
*
topLeftLayout = new QHBoxLayout;
22 topLeftLayout->addWidget(label);
23 topLeftLayout->addWidget(lineEdit);
24 QVBoxLayout
*
leftLayout = new QVBoxLayout;
25 leftLayout->addLayout(topLeftLayout);
26 leftLayout->addWidget(caseCheckBox);
27 leftLayout->addWidget(backwardCheckBox);
28 QVBoxLayout
*
rightLayout = new QVBoxLayout;
29 rightLayout->addWidget(findButton);
30 rightLayout->addWidget(closeButton);
31 rightLayout->addStretch();
32 QHBoxLayout
*
mainLayout = new QHBoxLayout;
33 mainLayout->addLayout(leftLayout);
34 mainLayout->addLayout(rightLayout);
35 setLayout(mainLayout);
36 setWindowTitle(tr("Find"));
Las lneas 21 a 35 colocan los widgets hijos usando gestores de
posicionamiento.
Layouts pueden contener widgets y otros layouts.
Combinando QHBoxLayouts, QVBoxLayouts y QGridLayouts
podemos construir dialogos muy sosticados.
Los layouts no son widgets (heredan de QLayout que a su vez
hereda de QObject)
Al incluir un layout en otro (lneas 25, 33 y 34), los sublayouts se
hacen sus hijos.
Al instalar en el dialogo (lnea 35) el layout principal, este y todos
los widgets de la jerarqua de layouts se hacen hijos del dialogo
3 CREACI

ON DE DI

ALOGOS 16
37 setFixedHeight(sizeHint().height());
38 }
En la lnea 37 establecemos un tama no vertical jo para el dialogo
(QWidget.sizeHint() devuelve el tama no ideal para un widget).
No necesitamos a nadir un destructor a FindDialog pues, aunque
siempre se ha usado new para crear los widgets y layouts del dialogo,
Qt automaticamente borra los objetos hijo cuando el padre se destruye.
Cuando se crea un objeto indicando el padre, el padre a nade el
objeto a su lista de hijos.
Si el padre es borrado, se borran autom aticamente la lista de hijos,
y as recursivamente.
Ademas, cuando el widget padre se borra, desaparece de pantalla
este, y los hijos que tenga.
Esto hace que en la practica, no haga falta que borremos los
objetos, salvo los creados con new que no tengan padre.
3 CREACI

ON DE DI

ALOGOS 17
39 void FindDialog::findClicked()
40 {
41 QString text = lineEdit->text();
42 Qt::CaseSensitivity cs =
43 caseCheckBox->isChecked() ? Qt::CaseSensitive
44 : Qt::CaseInsensitive;
45 if (backwardCheckBox->isChecked()) {
46 emit findPrevious(text, cs);
47 } else {
48 emit findNext(text, cs);
49 }
50 }
51 void FindDialog::enableFindButton(const QString &text)
52 {
53 findButton->setEnabled(!text.isEmpty());
54 }
El slot findClicked() (llamado al pinchar el boton Find) emite las
se nales findPrevious() o findNext(), dependiendo de si esta o no
seleccionado Search backward.
La palabra emit es tambien propia de Qt, y convertida a codigo C++
por el preprocesador.
El slot enableFindButton() (llamado cuando el usuario cambia el
texto de lineEdit) activa el boton Find si hay alg un texto en
lineEdit
Ejemplo de dialogo: chap02/nd/main.cpp
1 #include <QApplication>
2 #include "finddialog.h"
3 int main(int argc, char
*
argv[])
4 {
5 QApplication app(argc, argv);
6 FindDialog
*
dialog = new FindDialog;
7 dialog->show();
8 return app.exec();
9 }
3 CREACI

ON DE DI

ALOGOS 18
3.2. Mas sobre se nales y slots
La sentencia connect() tiene la siguiente forma:
connect(sender, SIGNAL(signal), receiver, SLOT(slot));
donde sender y receiver son punteros a QObjects.
Las macros SIGNAL() y SLOT() convierten su argumento en un string.
Una se nal puede conectarse a varios slots:
connect(slider, SIGNAL(valueChanged(int)),
spinBox, SLOT(setValue(int)));
connect(slider, SIGNAL(valueChanged(int)),
this, SLOT(updateStatusBarIndicator(int)));
Varias se nales pueden conectarse al mismo slot:
connect(lcd, SIGNAL(overflow()),
this, SLOT(handleMathError()));
connect(calculator, SIGNAL(divisionByZero()),
this, SLOT(handleMathError()));
Una se nal puede conectarse a otra se nal:
connect(lineEdit, SIGNAL(textChanged(const QString &)),
this, SIGNAL(updateRecord(const QString &)));
Las conexiones pueden borrarse (aunque no se usa demasiado):
disconnect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));
Para conectar una se nal con un slot u otra se nal, ambos deben tener los
mismos tipos de parametros y en el mismo orden:
connect(ftp, SIGNAL(rawCommandReply(int, const QString &)),
this, SLOT(processReply(int, const QString &)));
Como excepci on, si una se nal tiene mas parametros que el slot, se
ignoran los parametros adicionales:
connect(ftp, SIGNAL(rawCommandReply(int, const QString &)),
this, SLOT(checkErrorCode(int)));
No deben incluirse los nombres de los parametros de las se nales o slots
al usar connect.
4 UN VISTAZO A LOS WIDGETS DE QT 19
4. Un vistazo a los widgets de Qt
4.1. Botones
Pueden etiquetarse con texto o icono (un pixmap)
QPushButton: Representa un boton de comando (los normales).
QPushButton *button = new QPushButton("&Cancel", this);
Normalmente se conectan a un slot, cuando emiten la se nal
clicked()
Tambien tienen disponibles las se nales pressed(), released() y
toggled().
Se les puede asociar un men u popup con setPopup()
QToolButton: Tambien representa un boton de comando, aunque
suelen usarse como botones toggle
Normalmente muestran una imagen (QIcon) y no una etiqueta.
Se crean normalmente al crear un QAction (objeto que luego puede
incluirse en un men u y en la barra de herramientas)
QCheckBox y QRadioButton
Los QCheckBox suelen aparecer en grupos donde podremos
seleccionar cero, una o varias opciones.
4 UN VISTAZO A LOS WIDGETS DE QT 20
Los QRadioButton tambien suelen colocarse en un grupo con varios
QRadioButton donde podremos seleccionar exactamente uno.
QButtonGroup* bgroup = new QButtonGroup( "Heading", parent);
QRadioButton* radio1 = new QRadioButton( "Choice1", bgroup);
QRadioButton* radio2 = new QRadioButton( "Choice2", bgroup);
QRadioButton* radio3 = new QRadioButton( "Choice3", bgroup);
Para comprobar si esta seleccionado: bool isChecked()
Para seleccionarlos: setChecked(bool)
Tienen disponibles las se nales clicked(), pressed(), released()
y toggled().
4.2. Contenedores
Son widgets que contienen otros widgets:
QGroupBox
QFrame
4 UN VISTAZO A LOS WIDGETS DE QT 21
QTabWidget: Es un contenedor multipagina.
QToolBox: Otro contenedor multipagina.
4.3. Visores de items
QListView (basado en el marco model/view) y QListWidget: Muestra
una lista de items
4 UN VISTAZO A LOS WIDGETS DE QT 22
QTreeView (basado en el marco model/view) y QTreeWidget:
QTableView (basado en el marco model/view) y QTableWidget
4.4. Widgets para mostrar informacion
QLabel: Permite mostrar texto simple, html o imagenes.
QProgressBar
QLCDNumber: Muestra un n umero con display LCD
QTextBrowser: Es un subclase de QTextEdit y soporta html basico
(listas, tablas, imagenes e hiperenlaces).
4 UN VISTAZO A LOS WIDGETS DE QT 23
4.5. Widgets de entrada
QSpinBox: Se usa para introducir valores enteros o de un conjunto
discreto.
QDoubleSpinBox:
QComboBox: Es la combinacion de un boton con una lista (popup)
QDateEdit, QTimeEdit, QDateTimeEdit: Permiten introducir fechas, la
hora, o ambos simultaneamente.
QCalendarWidget: Permite introducir fechas.
4 UN VISTAZO A LOS WIDGETS DE QT 24
QScrollbar: Barra de scroll horizontal o vertical
Permite acceder a partes de un documento que es mayor que el widget
usado para mostrarlo.
QSlider: Deslizador (slider) horizontal o vertical
Permite mover la barra para traducir su posicion a un rango
determinado de valores enteros.
QTextEdit: Es un visor/editor WYSIWYG que soporta texto
enriquecido usando etiquetas HTML.
QLineEdit: Editor de texto de una sola lnea.
QDial: Permite dar un valor en un rango determinado (por ejemplo
grados 0-360).
4 UN VISTAZO A LOS WIDGETS DE QT 25
QFontComboBox: Permite introducir un font
4.6. Dialogos predenidos
QColorDialog
QFontDialog
QPageSetupDialog
4 UN VISTAZO A LOS WIDGETS DE QT 26
QFileDialog
Ejemplo de QFileDialog:
chap03/spreadsheet/mainwindow.cpp
void MainWindow::open()
{
if (okToContinue()) {
QString fileName = QFileDialog::getOpenFileName(this,
tr("Open Spreadsheet"), ".",
tr("Spreadsheet files (
*
.sp)"));
if (!fileName.isEmpty())
loadFile(fileName);
}
}
bool MainWindow::saveAs()
{
QString fileName = QFileDialog::getSaveFileName(this,
tr("Save Spreadsheet"), ".",
tr("Spreadsheet files (
*
.sp)"));
if (fileName.isEmpty())
return false;
return saveFile(fileName);
}
Los argumentos de getOpenFileName() y getSaveFileName() son:
Widget padre: Los dialogos siempre son ventanas de alto nivel, pero
si tienen padre, al mostrarse aparecen centrados encima del padre.
Ttulo
Directorio inicial
Filtros: Texto descriptivo y un patron. Ejemplo:
tr("Spreadsheet files (*.sp)\n"
"Comma-separated values files (*.csv)\n"
"Lotus 1-2-3 files (*.wk1 *.wks)")
4 UN VISTAZO A LOS WIDGETS DE QT 27
QPrintDialog
QMessageBox
4 UN VISTAZO A LOS WIDGETS DE QT 28
Ejemplo de QMessageBox:
chap03/spreadsheet/mainwindow.cpp
bool MainWindow::okToContinue()
{
if (isWindowModified()) {
int r = QMessageBox::warning(this, tr("Spreadsheet"),
tr("The document has been modified.\n"
"Do you want to save your changes?"),
QMessageBox::Yes | QMessageBox::Default,
QMessageBox::No,
QMessageBox::Cancel | QMessageBox::Escape);
if (r == QMessageBox::Yes) {
return save();
} else if (r == QMessageBox::Cancel) {
return false;
}
}
return true;
}
4 UN VISTAZO A LOS WIDGETS DE QT 29
QErrorMessage
QInputDialog:

Util para introducir una lnea de texto o n umero.
QProgressDialog
5 LA VENTANA PRINCIPAL 30
5. La ventana principal
La ventana principal de una aplicacion se crea con una subclase de
QMainWindow (que hereda de QWidget).
Puede crearse tambien con Qt Designer.
La ventana puede estar compuesta de:
Una barra de men us: QMenuBar
Una o varias barras de utilidades: QToolBar
Estan formados por botones que suelen etiquetarse con imagenes.
Area principal de la aplicacion (central widget).
Barra de estado: QStatusBar
5 LA VENTANA PRINCIPAL 31
5.1. Denici on de la clase ventana principal
Crearemos una clase que herede de QMainWindow
Ejemplo: chap03/spreadsheet/mainwindow.h
1 #ifndef MAINWINDOW_H
2 #define MAINWINDOW_H
3 #include <QMainWindow>
4 class QAction;
5 class QLabel;
6 class FindDialog;
7 class Spreadsheet;
8 class MainWindow : public QMainWindow
9 {
10 Q_OBJECT
11 public:
12 MainWindow();
13 protected:
14 void closeEvent(QCloseEvent
*
event);
15 private slots:
16 void newFile();
17 void open();
18 bool save();
19 bool saveAs();
20 void find();
21 void goToCell();
22 void sort();
23 void about();
24 void openRecentFile();
25 void updateStatusBar();
26 void spreadsheetModified();
27 private:
28 void createActions();
29 void createMenus();
30 void createContextMenu();
31 void createToolBars();
32 void createStatusBar();
33 void readSettings();
34 void writeSettings();
35 bool okToContinue();
36 bool loadFile(const QString &fileName);
37 bool saveFile(const QString &fileName);
5 LA VENTANA PRINCIPAL 32
38 void setCurrentFile(const QString &fileName);
39 void updateRecentFileActions();
40 QString strippedName(const QString &fullFileName);
41 Spreadsheet
*
spreadsheet;
42 FindDialog
*
findDialog;
43 QLabel
*
locationLabel;
44 QLabel
*
formulaLabel;
45 QStringList recentFiles;
46 QString curFile;
47 enum { MaxRecentFiles = 5 };
48 QAction
*
recentFileActions[MaxRecentFiles];
49 QAction
*
separatorAction;
50 QMenu
*
fileMenu;
51 QMenu
*
editMenu;
52 QMenu
*
selectSubMenu;
53 QMenu
*
toolsMenu;
54 QMenu
*
optionsMenu;
55 QMenu
*
helpMenu;
56 QToolBar
*
fileToolBar;
57 QToolBar
*
editToolBar;
58 QAction
*
newAction;
59 QAction
*
openAction;
60 QAction
*
saveAction;
61 QAction
*
saveAsAction;
62 QAction
*
exitAction;
63 QAction
*
cutAction;
64 QAction
*
copyAction;
65 QAction
*
pasteAction;
66 QAction
*
deleteAction;
67 QAction
*
selectRowAction;
68 QAction
*
selectColumnAction;
69 QAction
*
selectAllAction;
70 QAction
*
findAction;
71 QAction
*
goToCellAction;
72 QAction
*
recalculateAction;
73 QAction
*
sortAction;
74 QAction
*
showGridAction;
75 QAction
*
autoRecalcAction;
76 QAction
*
aboutAction;
77 QAction
*
aboutQtAction;
78 };
79 #endif
5 LA VENTANA PRINCIPAL 33
El widget spreadsheet de la clase Spreadsheet (subclase de
QTableWidget) sera el widget central de la ventana principal.
La funcion closeEvent() es una funcion virtual de QWidget que se
llama automaticamente cuando el usuario cierra la ventana.
5.2. Constructor de la clase
Ejemplo: chap03/spreadsheet/mainwindow.cpp
#include <QtGui>
#include "finddialog.h"
#include "gotocelldialog.h"
#include "mainwindow.h"
#include "sortdialog.h"
#include "spreadsheet.h"
MainWindow::MainWindow()
{
spreadsheet = new Spreadsheet;
setCentralWidget(spreadsheet);
createActions();
createMenus();
createContextMenu();
createToolBars();
createStatusBar();
readSettings();
findDialog = 0;
setWindowIcon(QIcon(":/images/icon.png"));
setCurrentFile("");
}
5.3. El icono de la aplicaci on y mecanismo de recursos
A una ventana principal se le puede asociar un icono con
setWindowIcon(QIcon).
Los mecanismos que permiten usar imagenes en Qt son:
Cargar las imagenes en tiempo de ejecucion de cheros.
Incluir (con #include) en el codigo fuente cheros XPM.
5 LA VENTANA PRINCIPAL 34
Usar el mecanimo de recursos.
En este caso la imagen del icono se ha proporcionado mediante el
mecanismo de recursos de Qt.
Se necesita crear un chero de recursos (spreadsheet.qrc):
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>images/icon.png</file>
...
<file>images/gotocell.png</file>
</qresource>
</RCC>
El chero de recursos se debe incluir en el chero de proyecto .pro:
RESOURCES = spreadsheet.qrc
En el codigo fuente nos referiremos a cada recurso anteponiendo :/
5.4. Creaci on de men us y toolbars
Los men us los crearemos con el mecanismo de acciones.
Una accion es un item que puede a nadirse a varios men us y toolbars.
Las etapas para crear los men us y toolbars son:
Crear e inicializar las acciones.
Crear los men us y colocarle las acciones.
Crear los toolbars y colocarle las acciones.
5 LA VENTANA PRINCIPAL 35
5.4.1. Creacion de las acciones
Las acciones suelen crearse como hijas de la ventana principal
Cada accion puede tener asociado un texto, un icono, una tecla
aceleradora, un texto para la barra estado y un tooltip (si no ponemos
un tooltip con setToolTip() se usara el texto ).
Ejemplo: chap03/spreadsheet/mainwindow.cpp
void MainWindow::createActions()
{
newAction = new QAction(tr("&New"), this);
newAction->setIcon(QIcon(":/images/new.png"));
newAction->setShortcut(tr("Ctrl+N"));
newAction->setStatusTip(tr("Create a new spreadsheet file"));
connect(newAction, SIGNAL(triggered()), this, SLOT(newFile()));
...
showGridAction = new QAction(tr("&Show Grid"), this);
showGridAction->setCheckable(true);
showGridAction->setChecked(spreadsheet->showGrid());
showGridAction->setStatusTip(tr("Show or hide the "
"spreadsheets grid"));
connect(showGridAction, SIGNAL(toggled(bool)),
spreadsheet, SLOT(setShowGrid(bool)));
...
aboutQtAction = new QAction(tr("About &Qt"), this);
aboutQtAction->setStatusTip(tr("Show the Qt librarys "
"About box"));
connect(aboutQtAction, SIGNAL(triggered()),
qApp, SLOT(aboutQt()));
La clase QActionGroup permite denir acciones mutuamente exclusivas.
El slot aboutQt() de qApp muestra un dialogo about sobre la version
usada de Qt.
5 LA VENTANA PRINCIPAL 36
5.4.2. Creacion de los men us
El metodo QMainWindow::menuBar() crea el QMenuBar (barra de
men us) la primera vez que se llama.
El metodo QMenuBar::addMenu() crea un QMenu y lo a nade a la barra
de men us.
Ejemplo: chap03/spreadsheet/mainwindow.cpp
void MainWindow::createMenus()
{
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(newAction);
fileMenu->addAction(openAction);
fileMenu->addAction(saveAction);
fileMenu->addAction(saveAsAction);
separatorAction = fileMenu->addSeparator();
for (int i = 0; i < MaxRecentFiles; ++i)
fileMenu->addAction(recentFileActions[i]);
fileMenu->addSeparator();
fileMenu->addAction(exitAction);
Los submen us se crean tambien con QMenu::addMenu()
Ejemplo: chap03/spreadsheet/mainwindow.cpp
editMenu = menuBar()->addMenu(tr("&Edit"));
editMenu->addAction(cutAction);
editMenu->addAction(copyAction);
editMenu->addAction(pasteAction);
editMenu->addAction(deleteAction);
selectSubMenu = editMenu->addMenu(tr("&Select"));
selectSubMenu->addAction(selectRowAction);
selectSubMenu->addAction(selectColumnAction);
selectSubMenu->addAction(selectAllAction);
5 LA VENTANA PRINCIPAL 37
5.4.3. Creacion de men us contextuales
Cualquier widget puede tener asociado una lista de QActions que
pueden usarse por ejemplo para construir un men u contextual.
En tal caso usaremos la siguiente llamada para indicar que el
men u contextual se construye con la lista de acciones:
widget->setContextMenuPolicy(Qt::ActionsContextMenu);
Ejemplo: chap03/spreadsheet/mainwindow.cpp
void MainWindow::createContextMenu()
{
spreadsheet->addAction(cutAction);
spreadsheet->addAction(copyAction);
spreadsheet->addAction(pasteAction);
spreadsheet->setContextMenuPolicy(Qt::ActionsContextMenu);
}
5.4.4. Creacion de barras de utilidades
Un toolbar se crea con QMainWindow::addToolBar()
Los elementos se a naden entonces con QToolBar::addAction()
Ejemplo: chap03/spreadsheet/mainwindow.cpp
void MainWindow::createToolBars()
{
fileToolBar = addToolBar(tr("&File"));
fileToolBar->addAction(newAction);
fileToolBar->addAction(openAction);
fileToolBar->addAction(saveAction);
editToolBar = addToolBar(tr("&Edit"));
editToolBar->addAction(cutAction);
editToolBar->addAction(copyAction);
editToolBar->addAction(pasteAction);
editToolBar->addSeparator();
editToolBar->addAction(findAction);
editToolBar->addAction(goToCellAction);
}
5 LA VENTANA PRINCIPAL 38
5.5. Creaci on de la barra de estado
La funcion QMainWindow::statusBar() crea la barra de estado la
primera vez que se llama.
A la barra de estado se a nadiran QLabels con
QStatusBar::addWidget(). Al a nadir QLabels a la barra de estado,
aquellas se hacen automaticamente hijas de la barra de estado.
Ejemplo: chap03/spreadsheet/mainwindow.cpp
1 void MainWindow::createStatusBar()
2 {
3 locationLabel = new QLabel(" W999 ");
4 locationLabel->setAlignment(Qt::AlignHCenter);
5 locationLabel->setMinimumSize(locationLabel->sizeHint());
6 formulaLabel = new QLabel;
7 formulaLabel->setIndent(3);
8 statusBar()->addWidget(locationLabel);
9 statusBar()->addWidget(formulaLabel, 1);
10 connect(spreadsheet, SIGNAL(
11 currentCellChanged(int, int, int, int)),
12 this, SLOT(updateStatusBar()));
13 connect(spreadsheet, SIGNAL(modified()),
14 this, SLOT(spreadsheetModified()));
15 updateStatusBar();
16 }
17
18 void MainWindow::updateStatusBar()
19 {
20 locationLabel->setText(spreadsheet->currentLocation());
21 formulaLabel->setText(spreadsheet->currentFormula());
22 }
23
24 void MainWindow::spreadsheetModified()
25 {
26 setWindowModified(true);
27 updateStatusBar();
28 }
5 LA VENTANA PRINCIPAL 39
En la lnea 9, statusBar()->addWidget(formulaLabel, 1) el segundo
parametro especica que el factor stretch para formulaLabel es 1.
O sea que al redimensionar la ventana donde esta incluida la barra
de estado, el espacio extra sera asignado entero a este widget.
Un valor de 0 (que es el valor por defecto), signica que se preere
no ser cambiado de tama no.
Puede mostrarse un mensaje durante un tiempo determinado con:
statusBar()->showMessage(tr("Mensaje"), 2000);
En la lnea 26, setWindowModified(true), se pone a true la
propiedad windowModified, que hace que aparezca en la barra de
ttulos alg un indicador de que el chero cargado actualmente se ha
cambiado y no se ha salvado a disco.
5.6. Implementaci on de los slots
Ejemplo: chap03/spreadsheet/mainwindow.cpp
void MainWindow::newFile()
{
if (okToContinue()) {
spreadsheet->clear();
setCurrentFile("");
}
}
bool MainWindow::okToContinue()
{
if (isWindowModified()) {
int r = QMessageBox::warning(this, tr("Spreadsheet"),
tr("The document has been modified.\n"
"Do you want to save your changes?"),
QMessageBox::Yes | QMessageBox::Default,
QMessageBox::No,
QMessageBox::Cancel | QMessageBox::Escape);
if (r == QMessageBox::Yes) {
return save();
} else if (r == QMessageBox::Cancel) {
return false;
5 LA VENTANA PRINCIPAL 40
}
}
return true;
}
5 LA VENTANA PRINCIPAL 41
Cuando se asigna un slot a varias acciones, probablemente necesitemos
usar QObject::sender():
Ejemplo: chap03/spreadsheet/mainwindow.cpp
void MainWindow::openRecentFile()
{
if (okToContinue()) {
QAction
*
action = qobject_cast<QAction
*
>(sender());
if (action)
loadFile(action->data().toString());
}
}
sender() devuelve un puntero al objeto que envo la se nal, cuando es
llamado desde un slot que se activo con una se nal.
qobject_cast<> hace un casting dinamico similar al casting de C++
dynamic_cast<T>
Cada Action puede tener asociado un dato de tipo QVariant con
action->setData(dato). En este caso se asocio un QString.
5 LA VENTANA PRINCIPAL 42
5.7. Introducci on al manejo de eventos de bajo nivel
Eventos tales como pulsacion de botones de raton, movimiento de raton
se conocen como eventos de bajo nivel o sintacticos.
En Qt se controlan creando subclases de alg un Widget Qt, y
sobreescribiendo el metodo virtual correspondiente de QWidget:
mousePressEvent(QMouseEvent*)
mouseMoveEvent(QMouseEvent*)
paintEvent(QPaintEvent*)
resizeEvent(QResizeEvent*)
closeEvent(QCloseEvent *)
keyPressEvent(QKeyEvent * event )
Cuando el usuario cierra la ventana se llama automaticamente a la
funcion closeEvent(QCloseEvent *), que podemos sobreescribir para
adaptarla a nuestras necesidades:
Ejemplo: chap03/spreadsheet/mainwindow.cpp
void MainWindow::closeEvent(QCloseEvent
*
event)
{
if (okToContinue()) {
writeSettings();
event->accept();
} else {
event->ignore();
}
}
5 LA VENTANA PRINCIPAL 43
5.8. Uso de nuestros dialogos
Los dialogos que hemos implementado pueden usarse ahora desde la
ventana principal:
Ejemplo con dialogo no modal:
chap03/spreadsheet/mainwindow.cpp
void MainWindow::find()
{
if (!findDialog) {
findDialog = new FindDialog(this);
connect(findDialog, SIGNAL(findNext(const QString &,
Qt::CaseSensitivity)),
spreadsheet, SLOT(findNext(const QString &,
Qt::CaseSensitivity)));
connect(findDialog, SIGNAL(findPrevious(const QString &,
Qt::CaseSensitivity)),
spreadsheet, SLOT(findPrevious(const QString &,
Qt::CaseSensitivity)));
}
findDialog->show();
findDialog->activateWindow();
}
5 LA VENTANA PRINCIPAL 44
Los dialogos modales suelen crearse en la pila en lugar de con memora
dinamica, ya que no se necesitan una vez que se usan:
Ejemplo con dialogo modal: chap03/spreadsheet/mainwindow.cpp
void MainWindow::goToCell()
{
GoToCellDialog dialog(this);
if (dialog.exec()) {
QString str = dialog.lineEdit->text().toUpper();
spreadsheet->setCurrentCell(str.mid(1).toInt() - 1,
str[0].unicode() - A);
}
}
Podemos asociar nuestro propio dialogo about usando la clase
QMessageBox:
Ejemplo con dialogo modal: chap03/spreadsheet/mainwindow.cpp
void MainWindow::about()
{
QMessageBox::about(this, tr("About Spreadsheet"),
tr("<h2>Spreadsheet 1.1</h2>"
"<p>Copyright &copy; 2006 Software Inc."
"<p>Spreadsheet is a small application that "
"demonstrates QAction, QMainWindow, QMenuBar, "
"QStatusBar, QTableWidget, QToolBar, and many other "
"Qt classes."));
}
5 LA VENTANA PRINCIPAL 45
5.9. Almacenamiento de la conguraci on de la
aplicaci on
Qt permite almacenar de forma persistente las propiedades de la
conguracion actual de una aplicacion mediante la clase QSettings:
tama no de ventana, posicion, opciones, ultimos cheros abiertos, etc
Las propiedades se almacenan en un chero que depende de la
plataforma:
En Windows se usa el registro del sistema.
En Unix se usan cheros de texto. Por ejemplo, en mi distribucion
de linux se colocan en el directorio $HOME/.config
Para usarlo tendremos que crear un objeto de QSettings de la
siguiente forma:
QSettings settings("Micompa~nia", "Nombre de mi aplicacion");
QSettings almacena cada propiedad mediante un nombre de la
propiedad (QString) y un valor (QVariant)
Para escribir un valor usaremos setValue(QString,QVariant)
settings.setValue("editor/Margen", 68);
Para leer el valor usaremos value(QString propiedad) o
value(QString propiedad,QVariant valorpordefecto):
int margin = settings.value("editor/Margen").toInt();
Ejemplo: chap03/spreadsheet/mainwindow.cpp
void MainWindow::writeSettings()
{
QSettings settings("Software Inc.", "Spreadsheet");
settings.setValue("geometry", geometry());
settings.setValue("recentFiles", recentFiles);
settings.setValue("showGrid", showGridAction->isChecked());
settings.setValue("autoRecalc", autoRecalcAction->isChecked());
}
5 LA VENTANA PRINCIPAL 46
Ejemplo: chap03/spreadsheet/mainwindow.cpp
void MainWindow::readSettings()
{
QSettings settings("Software Inc.", "Spreadsheet");
QRect rect = settings.value("geometry",
QRect(200, 200, 400, 400)).toRect();
move(rect.topLeft());
resize(rect.size());
recentFiles = settings.value("recentFiles").toStringList();
updateRecentFileActions();
bool showGrid = settings.value("showGrid", true).toBool();
showGridAction->setChecked(showGrid);
bool autoRecalc = settings.value("autoRecalc", true).toBool();
autoRecalcAction->setChecked(autoRecalc);
}
5.10. Implementaci on del widget central
El area central de una ventana principal puede ocuparse con cualquier
widget. Por ejemplo:
Con un widget Qt estandar: QTableWidget, QTextEdit, etc
Con un widget optimizado: una subclase de alg un tipo de widget.
Con un widget QWidget y un manejador de posicionamiento para
incluir otros widgets.
Con un widget QWorkspace (para aplicaciones MDI).
Ejemplo: chap03/spreadsheet/mainwindow.cpp
MainWindow::MainWindow()
{
spreadsheet = new Spreadsheet;
setCentralWidget(spreadsheet);
...
}
6 SISTEMA DE DIBUJO DE QT 47
6. Sistema de dibujo de Qt
El sistema de dibujo de Qt esta basado en las clases QPainter,
QPaintDevice y QPaintEngine:
QPainter se usa para llamar a las primitivas de dibujo: puntos,
lneas, rectangulos, elipses, arcos, polgonos, curvas de Bezier,
pixmaps, imagenes, texto, etc.
QPaintDevice es la abstraccion de un espacio bidimensional donde
podemos dibujar usando un objeto QPainter.
QPainter puede actuar sobre cualquier objeto que herede de la
clase QPaintDevice.
QPaintEngine proporciona el interfaz que usa el objeto QPainter
para dibujar en distintos dispositivos.
Esta clase es usada internamente por Qt, y los programadores
no la usaran salvo que quieran programar nuevos dispositivos.
Puede usarse tanto para dibujar en dispositivos de dibujo (QWidget,
QPixmap, o QImage) como en dispositivos de impresion (junto con la
clase QPrinter) o para generar cheros pdf.
6.1. Dibujar con QPainter
Para comenzar a dibujar en un dispositivo de dibujo (por ejemplo un
widget) creamos un objeto QPainter pasandole al constructor un
puntero al dispositivo. Por ejemplo:
void MyWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
...
}
6 SISTEMA DE DIBUJO DE QT 48
Con el objeto QPainter podemos dibujar varias formas:
6 SISTEMA DE DIBUJO DE QT 49
El resultado de cada primitiva graca depende de los atributos del
QPainter. Los mas importantes son:
Pincel (pen): Se usa para dibujar lneas y bordes de formas
(rectangulos, elipses, etc). Consiste de un color (QColor), una anchura,
estilo de lnea, cap style y join style.
Brocha (brush): Patron usado para rellenar formas geometricas.
Consiste normalmente de un color y un estilo, pero puede ser tambien
una textura (pixmap que se repite innitamente) o un gradiente.
Font: Se usa al dibujar texto. Contiene muchos atributos tal como la
familia y el tama no de punto.
Tales atributos pueden modicarse en cualquier momento con
setPen(), setBrush y setFont().
Estilos Cap y join
Estilos de pincel
6 SISTEMA DE DIBUJO DE QT 50
Estilos de brocha
Ejemplo 1:
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(Qt::black, 12, Qt::DashDotLine, Qt::RoundCap));
painter.setBrush(QBrush(Qt::green, Qt::SolidPattern));
painter.drawEllipse(80, 80, 400, 240);
La llamada a setRenderHint() activa el antialiasing, que hace que
se usen diferentes intesidades de color en las fronteras para reducir la
distorsion visual al convertir las fronteras de una gura en pixels.
Ver ejemplo de Antialiasing en: Aplicacion concentriccircles.
6 SISTEMA DE DIBUJO DE QT 51
Ejemplo 2:
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(Qt::black, 15, Qt::SolidLine, Qt::RoundCap,
Qt::MiterJoin));
painter.setBrush(QBrush(Qt::blue, Qt::DiagCrossPattern));
painter.drawPie(80, 80, 400, 240, 60 * 16, 270 * 16);
Ejemplo 3:
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
QPainterPath path;
path.moveTo(80, 320);
path.cubicTo(200, 80, 320, 80, 480, 320);
painter.setPen(QPen(Qt::black, 8));
painter.drawPath(path);
La clase QPainterPath permite especicar un dibujo vectorial
mediante la union de varios elementos gracos basicos: lneas rectas,
elipses, polgonos, arcos, curvas cuadraticas o de Bezier, y otros objetos
QPainterPath.
El objeto QPainterPath especica el borde de una gura, que puede
rellenarse si usamos una brocha en el QPainter
6 SISTEMA DE DIBUJO DE QT 52
6.2. Otros atributos del QPainter
Brocha del background: Es la brocha usada al dibujar texto opaco,
lneas tipo stippled y bitmaps. Esta brocha no tiene efecto en modo de
background transparente (el de por defecto).
setBackgroundBrush(QBrush)
setBackgroundMode( BGMode): Establece el modo de background
(Qt::TransparentMode, Qt::OpaqueMode)
Origen de la brocha: Punto inicial para patrones de rellenado con la
brocha (esquina superior izquierda normalmente).
Mascara de recorte (clip region):

Area del dispositivo que se
afectara por las primitivas gracas.
Viewport, window y world matrix: Determinan como transformar
las coordenadas del QPainter en coordenadas fsicas del dispositivo.
Modo de composicion: Especica como combinar los pxeles
existentes con los que se van a dibujar.
setCompositionMode(CompositionMode)
6.3. Transformaciones
El sistema de coordenadas por defecto de QPainter tiene su origen
(0, 0) en la esquina superior izquierda.
Las coordenadas x se incrementan hacia la derecha y las y hacia abajo.
Cada pixel ocupa un area de tama no 1 1.
6 SISTEMA DE DIBUJO DE QT 53
Es posible modicar el sistema de coordenadas por defecto usando el
viewport, window y world matrix
El viewport es un rectangulo que especica las coordenadas fsicas.
La window especica el mismo rectangulo pero en coordenadas logicas.
Cuando dibujamos con una primitiva graca, se usan coordenadas
logicas, que se trasladan en coordenadas fsicas usando el viewport y
window actuales.
Por defecto, ambos rectangulos coinciden con el rectangulo del
dispositivo.
Ejemplo: Si el dispositivo es un widget de 320 200, el viewport y
window son tambien un rectangulo de 320 200 con su esquina superior
izquierda en la posicion (0, 0).
En este caso, el sistema de coordenadas fsico y logico son el mismo.
Este sistema hace que el codigo para dibujar pueda hacerse
independiente del tama no o resolucion del dispositivo.
Ejemplo: En el caso de antes, podramos denir el sistema de
coordenadas logicas en el rango (50, 50) a (50, 50), siendo (0, 0) el
centro mediante:
painter.setWindow(-50, -50, 100, 100);
Ahora la coordenada logica (50, 50) corresponde con la coordenada
fsica (0, 0)
La coordenada logica (50, 50) corresponde con la coordenada fsica
(320, 200)
6 SISTEMA DE DIBUJO DE QT 54
La world matrix es una matriz de trasformacion que es aplicada
adicionalmente ademas del viewport y window.
Permite hacer las siguientes trasformaciones a los items que dibujamos:
trasladar, escalar, rotar y girar (shear). (Ver programa de ejemplo
affine en /usr/lib/qt4/demos/affine)
Ejemplo: Para dibujar un texto rotado 45
o
usaremos
QMatrix matrix;
matrix.rotate(45.0);
painter.setMatrix(matrix);
painter.drawText(rect, Qt::AlignCenter, tr("Revenue"));
Si especicamos varias trasformaciones, se aplicaran en el orden que
demos.
Ejemplo: Si queremos usar el punto (10, 20) como punto de rotacion,
podemos trasladar primero la window, hacer la rotacion y trasladar la
window de nuevo a su posicion original.
QMatrix matrix;
matrix.translate(-10.0, -20.0);
matrix.rotate(45.0);
matrix.translate(+10.0, +20.0);
painter.setMatrix(matrix);
painter.drawText(rect, Qt::AlignCenter, tr("Revenue"));
Una forma mas simple de especicar trasformaciones es usando los
metodos translate(), scale(), rotate(), y shear() de QPainter.
Ejemplo:
painter.translate(-10.0, -20.0);
painter.rotate(45.0);
painter.translate(+10.0, +20.0);
painter.drawText(rect, Qt::AlignCenter, tr("Revenue"));
Pero si queremos usar varias veces las mismas trasformaciones, es mas
eciente almacenarlas en un objeto QMatrix y denir la window matrix
del objeto QPainter cuando se necesiten tales trasformaciones.
6 SISTEMA DE DIBUJO DE QT 55
6.4. Modos de composici on
Otra ventaja del motor de dibujo de Qt es que soporta modos de
composicion: forma en que se combian el fuente y destino.
QPainter::setCompositionMode()
Ejemplo de uso de un modo de composicion:
QImage resultImage = checkerPatternImage;
QPainter painter(&resultImage);
painter.setCompositionMode(QPainter::CompositionMode_Xor);
painter.drawImage(0, 0, butterflyImage);
6 SISTEMA DE DIBUJO DE QT 56
6.5. Rendering de alta calidad con QImage
En X11 y MAC OS X, el peso de dibujar en un QWidget o QPixmap
recae en el motor de dibujo de la plataforma nativa.
En X11 esto hace que la comunicacion cliente-servidor se minimice, ya
que los Pixmaps se almacenan en el servidor.
Pero tiene el inconveniente que se puede perder calidad en algunos
gracos en plataformas X11 que no tengan instalada la X Render
Extension:
Por ejempo: No se puede usar Antialiasing.
Si la calidad es mas importante que la eciencia podemos dibujar en un
QImage y copiar luego el resultado en pantalla
As, se usara el motor interno de Qt para dibujar.
Esto permite obtener el mismo resultado en cualquier plataforma.
Para ello, el objeto QImage debe crearse de tipo
QImage::Format_RGB32 o
QImage::Format_ARGB32_Premultiplied.
Ejemplo de uso de Antialiasing sin QImage:
void MyWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
draw(&painter);
}
6 SISTEMA DE DIBUJO DE QT 57
Ejemplo de uso de Antialiasing con QImage:
void MyWidget::paintEvent(QPaintEvent *event)
{
QImage image(size(), QImage::Format_ARGB32_Premultiplied);
QPainter imagePainter(&image);
imagePainter.initFrom(this);
imagePainter.setRenderHint(QPainter::Antialiasing, true);
imagePainter.eraseRect(rect());
draw(&imagePainter);
imagePainter.end();
QPainter widgetPainter(this);
widgetPainter.drawImage(0, 0, image);
}
6 SISTEMA DE DIBUJO DE QT 58
6.6. Clases QImage y QPixmap
Qt proporciona cuatro clases para manejar imagenes: QImage,
QPixmap, QBitmap y QPicture.
QImage: Esta dise nada y optimizada para operaciones de
entrada/salida, y para acceso y manipulacion directa a nivel de
pixel.
QPixmap: Esta dise nada y optimizada para mostrar imagenes en
pantalla.
QBitmap: Es una clase que hereda de QPixmap, para pixmaps de
profundidad 1.
QPicture: Es un dispositivo de dibujo que permite almacenar y
reproducir comandos QPainter.
Todos heredan de QPaintDevice por lo que QPainter podra usarse
directamente para dibujar sobre ellos.
6.6.1. Clase QImage
Una imagen puede ser cargada de un chero con el constructor o bien
con QImage::load()
El chero podra ser un chero normal (cargado en tiempo de ejecucion)
o uno leido con el sistema de recursos (en tiempo de compilacion).
Una imagen puede ser grabada en disco con save().
Por defecto Qt soporta los siguientes formatos:
6 SISTEMA DE DIBUJO DE QT 59
Las funciones para manipular una imagen dependen del formato
(QImage::Format) usado al crear el objeto QImage:
Imagenes de 32 bits usan valores ARGB.
Cada valor de pixel (32 bits) se descompone en 8 bits para la
intensidad de rojo, 8 para verde y 8 para azul, y 8 para nivel de
trasparencia.(el componente alpha u opacidad).
Por ejemplo: El color rojo puro se representara con:
QRgb red = qRgba(255, 0, 0, 255);
o bien con:
QRgb red = qRgb(255, 0, 0);
o bien con: QRgb red = 0xFFFF0000;
6 SISTEMA DE DIBUJO DE QT 60
Imagenes monocromo y de 8 bits usan valores basados en ndices
en la paleta de color.
Ahora el valor del pixel es un ndice en la tabla (paleta) de color de la
imagen.
6 SISTEMA DE DIBUJO DE QT 61
6.6.2. Clase QPixmap
Una imagen puede ser cargada de un chero con el constructor o bien
con QPixmap::load()
Un pixmap puede crearse con uno de los constructores o con las
funciones estaticas:
grabWidget(): Crea un pixmap con el contenido capturado de un
widget.
grabWindow(): Crea un pixmap con el contenido capturado de una
ventana.
Un QPixmap puede mostrarse en pantalla con un QLabel o alguna de las
subclases de QAbstractButton (como QPushButton y QToolButton):
QLabel tiene la propiedad pixmap y las funciones de acceso
pixmap() y setPixmap().
QAbstractButton tiene la propiedad icon (QIcon) y las funciones
de acceso icon() y setIcon().
Los datos de cada pixel solo pueden ser accedidos a traves de funciones
de la clase QPainter o convirtiendo el QPixmap en un QImage:
En un pixmap los datos de cada pixel son datos internos
manejados por el correspondiente manejador de ventanas (o
servidor X).
Un QPixmap puede convertirse en QImage con QPixmap::toImage()
Un QImage puede convertirse en QPixmap con QPixmap::fromImage()
6 SISTEMA DE DIBUJO DE QT 62
6.6.3. Clase QPicture
Ejemplo de grabacion de un QPicture en un chero:
QPicture picture;
QPainter painter;
painter.begin(&picture); // paint in picture
painter.drawEllipse(10,20, 80,70); // draw an ellipse
painter.end(); // painting done
picture.save("drawing.pic"); // save picture
Ejemplo de dibujo de un QPicture en un widget:
QPicture picture;
picture.load("drawing.pic"); // load picture
QPainter painter;
painter.begin(&myWidget); // paint in myWidget
painter.drawPicture(0, 0, picture); // draw the picture at (0,0)
painter.end(); // painting done
7 CREACI

ON DE WIDGETS OPTIMIZADOS 63
7. Creaci on de widgets optimizados
Es posible crear un widget optimizado con una subclase de alg un
widget Qt o directamente de QWidget.
Esto es preciso cuando necesitamos mas de lo que es posible
modicando las propiedades de un widget Qt o llamando a las
funciones que tenga disponibles.
7.1. Ejemplo: Un spin box hexadecimal
Un spin box hexadecimal: chap05/hexspinbox/hexspinbox.h
#ifndef HEXSPINBOX_H
#define HEXSPINBOX_H
#include <QSpinBox>
class QRegExpValidator;
class HexSpinBox : public QSpinBox
{
Q_OBJECT
public:
HexSpinBox(QWidget
*
parent = 0);
protected:
QValidator::State validate(QString &text, int &pos) const;
int valueFromText(const QString &text) const;
QString textFromValue(int value) const;
private:
QRegExpValidator
*
validator;
};
#endif
HexSpinBox hereda la mayora de su funcionalidad de QSpinBox.
A nade un constructor tpico y sobreescribe tres funciones virtuales de
QSpinBox.
7 CREACI

ON DE WIDGETS OPTIMIZADOS 64
Un spin box hexadecimal: chap05/hexspinbox/hexspinbox.cpp
#include <QtGui>
#include "hexspinbox.h"
HexSpinBox::HexSpinBox(QWidget
*
parent)
: QSpinBox(parent)
{
setRange(0, 255);
validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"), this);
}
Se pone como rango por defecto el intervalo 0 255 (0x00 a 0xFF) en
lugar del que tiene por defecto un QSpinBox (0 a 99).
La variable privada validator permitira validar si las entradas en el
editor de lneas del spin box, son validas:
Usaremos un QRegExpValidator que acepta entre 1 y 8
caracteres, cada uno del conjunto 0 a 9, A a F y a a
f.
Un spin box hexadecimal: chap05/hexspinbox/hexspinbox.cpp
QValidator::State HexSpinBox::validate(QString &text, int &pos) const
{
return validator->validate(text, pos);
}
La funcion QSpinBox::validate() es llamada por el QSpinBox para
ver si el texto introducido es valido.
QSpinBox::validate() puede devolver Invalid, Intermediate y
Acceptable.
La funcion QRegExpValidator::validate() nos permite devolver el
resultado deseado.
7 CREACI

ON DE WIDGETS OPTIMIZADOS 65
Un spin box hexadecimal: chap05/hexspinbox/hexspinbox.cpp
QString HexSpinBox::textFromValue(int value) const
{
return QString::number(value, 16).toUpper();
}
La funcion QSpinBox::textFromValue() convierte un valor entero en
un string.
QSpinBox la llama para actualizar el texto del editor de lneas, cuando
el usuario pulsa las echas ascendente o descendente.
Un spin box hexadecimal: chap05/hexspinbox/hexspinbox.cpp
int HexSpinBox::valueFromText(const QString &text) const
{
bool ok;
return text.toInt(&ok, 16);
}
La funcion valueFromText() realiza la conversion inversa, de string a
valor entero.
QSpinBox la llama cuando el usuario introduce un valor en el editor de
lneas y pulsa Enter.
7 CREACI

ON DE WIDGETS OPTIMIZADOS 66
7.2. Subclases de QWidget
Podemos crear widget optimizados combinando los widgets disponibles
(widgets Qt u otros widgets optimizados).
Si el widget que necesitamos no necesita denir sus propias se nales y
slots, y no sobreescribira ninguna funcion virtual, es posible que no
haga falta crear una nueva clase, y baste con combinar los widgets
disponibles.
En caso contrario crearemos una nueva clase que heredara de QWidget.
En ella sobreescribiremos algunos manejadores de eventos de bajo
nivel: paintEvent(), mousePressEvent(), etc.
Esto nos permitira controlar la apariencia y el comportamiento del
widget.
7.3. Ejemplo: Un editor de iconos
7 CREACI

ON DE WIDGETS OPTIMIZADOS 67
7.3.1. El chero .h
Un editor de iconos: chap05/iconeditor/iconeditor.h
#ifndef ICONEDITOR_H
#define ICONEDITOR_H
#include <QColor>
#include <QImage>
#include <QWidget>
class IconEditor : public QWidget
{
Q_OBJECT
Q_PROPERTY(QColor penColor READ penColor WRITE setPenColor)
Q_PROPERTY(QImage iconImage READ iconImage WRITE setIconImage)
Q_PROPERTY(int zoomFactor READ zoomFactor WRITE setZoomFactor)
public:
IconEditor(QWidget
*
parent = 0);
void setPenColor(const QColor &newColor);
QColor penColor() const { return curColor; }
void setZoomFactor(int newZoom);
int zoomFactor() const { return zoom; }
void setIconImage(const QImage &newImage);
QImage iconImage() const { return image; }
QSize sizeHint() const;
Q_PROPERTY() es una macro que permite declarar una propiedad
(campo especial de la clase que permite acceder a ella desde Qt
Designer).
Cada propiedad tiene un tipo (cualquiera soportado por QVariant),
una funcion de lectura, y opcionalmente una funcion de escritura.
La macro Q_OBJECT debe incluirse en la clase cuando denimos
propiedades.
7 CREACI

ON DE WIDGETS OPTIMIZADOS 68
Un editor de iconos: chap05/iconeditor/iconeditor.h
protected:
void mousePressEvent(QMouseEvent
*
event);
void mouseMoveEvent(QMouseEvent
*
event);
void paintEvent(QPaintEvent
*
event);
private:
void setImagePixel(const QPoint &pos, bool opaque);
QRect pixelRect(int i, int j) const;
QColor curColor;
QImage image;
int zoom;
};
#endif
IconEditor sobreescribe tres funciones virtuales protected de QWidget:
mousePressEvent(), mouseMoveEvent() y paintEvent().
Ademas dene dos funciones y tres variables privadas.
7 CREACI

ON DE WIDGETS OPTIMIZADOS 69
7.3.2. El chero .cpp
Un editor de iconos: chap05/iconeditor/iconeditor.cpp
#include <QtGui>
#include "iconeditor.h"
IconEditor::IconEditor(QWidget
*
parent)
: QWidget(parent)
{
setAttribute(Qt::WA_StaticContents);
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
curColor = Qt::black;
zoom = 8;
image = QImage(16, 16, QImage::Format_ARGB32);
image.fill(qRgba(0, 0, 0, 0));
}
Las variables que se inicializan en el constructor son:
curColor (color del pincel): asignada con color negro.
zoom (factor de zoom): inicializada a valor 8.
image (imagen del editor de iconos): inicializada con un QImage de
tama no 16 16 y formato ARGB (formato que soporta
semi-trasparencia) de 32 bis de profundidad.
La imagen es rellenada con un color blanco trasparente con
image.fill(qRgba(0, 0, 0, 0))
qRgba() devuelve el color con el tipo QRgb.
7 CREACI

ON DE WIDGETS OPTIMIZADOS 70
Un editor de iconos: chap05/iconeditor/iconeditor.cpp
QSize IconEditor::sizeHint() const
{
QSize size = zoom
*
image.size();
if (zoom >= 3)
size += QSize(1, 1);
return size;
}
sizeHint() es una funcion sobreescrita de QWidget que devuelve el
tama no ideal de un widget.
En este caso, devolvera el tama no de la imagen multiplicado por el
factor de zoom, y sumandole 1 si el factor de zoom es mayor a 2.
El size hint de un widget es sobre todo tenido en cuenta, cuando se
coloca el widget con un layout. Un layout manager intenta
respectarlo lo maximo posible.
La llamada a
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
en el constructor informa a cualquier layout manager que contenga este
widget que el tama no especicado con sizeHint() es realmente un
tama no mnimo.
7 CREACI

ON DE WIDGETS OPTIMIZADOS 71
Un editor de iconos: chap05/iconeditor/iconeditor.cpp
void IconEditor::setPenColor(const QColor &newColor)
{
curColor = newColor;
}
void IconEditor::setIconImage(const QImage &newImage)
{
if (newImage != image) {
image = newImage.convertToFormat(QImage::Format_ARGB32);
update();
updateGeometry();
}
}
La funcion setPenColor() dene un nuevo color para el pincel.
La funcion setIconImage() dene la imagen a editar.
La llamada a update() fuerza un repintado del widget usando la
nueva imagen.
La llamada QWidget::updateGeometry() informa al layout que
contenga este widget que el ha cambiado su tama no y qe adapte su
tama no al que tenga ahora el widget.
Un editor de iconos: chap05/iconeditor/iconeditor.cpp
void IconEditor::setZoomFactor(int newZoom)
{
if (newZoom < 1)
newZoom = 1;
if (newZoom != zoom) {
zoom = newZoom;
update();
updateGeometry();
}
}
La funcion setZoomFactor() establece un nuevo factor de zoom para
la imagen.
Las funciones penColor(), iconImage(), y zoomFactor() estan
implementadas como funciones inline en el chero de cabecera.
7 CREACI

ON DE WIDGETS OPTIMIZADOS 72
Un editor de iconos: chap05/iconeditor/iconeditor.cpp
void IconEditor::paintEvent(QPaintEvent
*
event)
{
QPainter painter(this);
if (zoom >= 3) {
painter.setPen(palette().windowText().color());
for (int i = 0; i <= image.width(); ++i)
painter.drawLine(zoom
*
i, 0,
zoom
*
i, zoom
*
image.height());
for (int j = 0; j <= image.height(); ++j)
painter.drawLine(0, zoom
*
j,
zoom
*
image.width(), zoom
*
j);
}
for (int i = 0; i < image.width(); ++i) {
for (int j = 0; j < image.height(); ++j) {
QRect rect = pixelRect(i, j);
if (!event->region().intersect(rect).isEmpty()) {
QColor color = QColor::fromRgba(image.pixel(i, j));
painter.fillRect(rect, color);
}
}
}
}
La funcion paintEvent() se llama cada vez que el widget necesita
repintarse:
Al aparecer por primera vez.
Cuando cambia su tama no.
Al ocultar otra ventana que lo tapaba parcialmente.
Por defecto, no hace nada, dejando blanco el widget.
Podemos forzar un repintado con:
Widget::update(): Encola el repintado hasta que Qt procese los
eventos de repintado.
QWidget::repaint(): Fuerza un repintado inmediato.
Cada widget tiene asociada una paleta (QPalette) que se puede
obtener con palette() (grupos de colores dependiendo del estado del
widget: Active, Inactive o Disabled).
7 CREACI

ON DE WIDGETS OPTIMIZADOS 73
En una paleta, podemos obtener la brocha (QBrush) usada para el
texto (foreground general) con windowText()
Un editor de iconos: chap05/iconeditor/iconeditor.cpp
QRect IconEditor::pixelRect(int i, int j) const
{
if (zoom >= 3) {
return QRect(zoom
*
i + 1, zoom
*
j + 1, zoom - 1, zoom - 1);
} else {
return QRect(zoom
*
i, zoom
*
j, zoom, zoom);
}
}
La funcion IconEditor::pixelRect() se usa desde paintEvent()
para construir un pixel aumentado con el zoom (un QRect).
Un editor de iconos: chap05/iconeditor/iconeditor.cpp
void IconEditor::mousePressEvent(QMouseEvent
*
event)
{
if (event->button() == Qt::LeftButton) {
setImagePixel(event->pos(), true);
} else if (event->button() == Qt::RightButton) {
setImagePixel(event->pos(), false);
}
}
Cuando el usuario pulsa un boton del raton, el sistema genera un
evento de raton, y llama a la funcion virtual mousePressEvent().
Un editor de iconos: chap05/iconeditor/iconeditor.cpp
void IconEditor::mouseMoveEvent(QMouseEvent
*
event)
{
if (event->buttons() & Qt::LeftButton) {
setImagePixel(event->pos(), true);
} else if (event->buttons() & Qt::RightButton) {
setImagePixel(event->pos(), false);
}
}
Los movimiento de raton (con un boton del raton pulsado) hacen que
el sistema llame a la funcion virtual mouseMoveEvent().
7 CREACI

ON DE WIDGETS OPTIMIZADOS 74
Si quisieramos que tambien se llame a la funcion anterior sin pulsar el
boton del raton usar: QWidget::setMouseTracking()
Un editor de iconos: chap05/iconeditor/iconeditor.cpp
void IconEditor::setImagePixel(const QPoint &pos, bool opaque)
{
int i = pos.x() / zoom;
int j = pos.y() / zoom;
if (image.rect().contains(i, j)) {
if (opaque) {
image.setPixel(i, j, penColor().rgba());
} else {
image.setPixel(i, j, qRgba(0, 0, 0, 0));
}
update(pixelRect(i, j));
}
}
La funcion setImagePixel() se llama desde mousePressEvent() y
mouseMoveEvent() para pintar o borrar un pixel.
El parametro pos es la posicion del raton en el widget.
En el constructor de IconEditor hemos usado la llamada:
setAttribute(Qt::WA_StaticContents);
Este atributo indica a Qt que el contenido del widget no cambia
cuando cambia su tama no al ser redimensionado, y que el contenido
queda anclado a la esquina superior izquierda del widget.
Normalmente cuando se redimensiona un widget, Qt genera un
evento de repintado para toda la parte visible del widget.
Con este atributo conseguimos reducir el repintado a la parte que
aparezca nueva.

También podría gustarte