Una de las ventajas que tiene la programación orientada a objetos es la reusabilidad del código ya hecho. Si creamos una clase, la depuramos exhaustivamente y la dejamos lista, podemos usarla como base para otros programas sin tener que rehacer el código. Una de los puntos a favor del toolkit Qt es que está hecho en C++ brindando todos los beneficios de este lenguaje junto a la programación orientada a objetos; y KDE, estando derivado de Qt también ofrece esta ventaja.
La mejor opción, desde mi punto de vista, al momento de crear interfaces visuales es, precisamente, el crear widgets a nuestra medida o derivar algún widget ya depurado e insertarlo en nuestra aplicación. Un widget es un objeto, que bien diseñado, puede contener cualquier objeto como otros widgets, incluso objetos más complejos como barras de herramientas, layouts, etc. Aprovechando esta facilidad, diseñaremos un widget sencillo con una barra de herramientas y un editor de texto para mostrar una frase. La barra de herramientas la haremos usando un método que nos permitirá ubicarla en cualquier widget, a diferencia del método que vimos en un artículo anterior, que solo se puede ubicar en el área destinada por KMainWindow para este propósito. A la barra de herramientas le colocaremos un ComboBox y un SpinBox para que veamos que la clase KToolBar nos permite colocar otras cosas además de botones y cómo hacer para colocarlos. Además de estos dos componentes, le añadiremos tres botones para recordar el uso de los slots; dos botones para modificar el tamaño de la letra del editor y un botón para terminar la aplicación. Una vez creado nuestro objeto, lo asignaremos como objeto principal de la aplicación para que sea mostrado al correr nuestro programa.
Este artículo será más una prueba de concepto que la realización de un programa con un fin útil. Para este artículo necesitaremos crear cuatro archivos para nuestra aplicación: kapp.pro, include/CWMiWidget.h, src/CWMiWidget.cpp y src/kapp.cpp. Acostumbro a anteponer al nombre del archivo una C para indicar que el archivo es una clase y CW para indicar que es una clase que produce un widget y, como habrán notado al leer otros artículos, no me gusta usar nombres con minúsculas solamente, ni tampoco uso Qt Designer para crear algún widget o alguna interfaz, por lo que si alguien prefiere hacer los widgets con este editor puede buscar en la web algún tutorial al respecto. El contenido de los archivos serían los siguientes:
Archivo CWMiWidget.h
#ifndef CWMIWIDGET_H
#define CWMIWIDGET_H
#include <qwidget.h>
class KComboBox;
class KIntSpinBox;
class KTextBrowser;
class QGridLayout;
class CWMiWidget : public QWidget {
Q_OBJECT
public:
CWMiWidget(QWidget *parent = 0, const char *name = 0);
private:
KComboBox *cmb;
KIntSpinBox *sp;
KTextBrowser *tb;
QGridLayout *Layout;
void CrearToolbar(void);
private slots:
void slotComboBox (int index);
void slotSpinBox (int num);
};
#endif
Archivo CWMiWidget.cpp
#include "CWMiWidget.h"
#include <kcombobox.h>
#include <kiconloader.h>
#include <knuminput.h>
#include <ktextbrowser.h>
#include <qdockarea.h>
#include <qlayout.h>
#include <qtoolbar.h>
#include <qtoolbutton.h>
CWMiWidget::CWMiWidget(QWidget *parent, const char *name) : QWidget(parent, name, Qt::WDestructiveClose) {
Layout = new QGridLayout(this, 2, 1, 5, 5, "CWMiWidget_Layout");
tb = new KTextBrowser(this, "CWMiWidget_tb");
CrearToolbar();
Layout->addWidget(tb, 1, 0);
setGeometry(0,0,400,400);
setCaption("CWMiWidget");
show();
}
void CWMiWidget::CrearToolbar(void) {
QDockArea *qda = new QDockArea(Qt::Horizontal, QDockArea::Normal, this, "CWMiWidget_qda");
QToolBar *qtb = new QToolBar ("ToolBar", NULL, qda, FALSE, "CWMiWidget_qtb");
cmb = new KComboBox(FALSE, qtb, "CWMiWidget_cmb");
for (int i = 1; i < 11; i++) cmb->insertItem(QString::number(i));
sp = new KIntSpinBox(1, 10, 1, 1, 10, qtb, "CWMiWidget_sp");
qtb->addSeparator();
new QToolButton(BarIcon("viewmag+.png"), "Aumentar tamaño de fuente",
"", tb, SLOT(zoomIn()), qtb, "tlbZoomIn");
new QToolButton(BarIcon("viewmag-.png"), "Disminuir tamaño de fuente",
"", tb, SLOT(zoomOut()), qtb, "tlbZoomOut");
qtb->addSeparator();
new QToolButton(BarIcon("exit.png"), "Cerrar ventana",
"", this, SLOT(close()), qtb, "tlbZoomOut");
Layout->addWidget(qda, 0, 0);
connect(cmb, SIGNAL(activated(int)), this, SLOT(slotComboBox(int)));
connect(sp, SIGNAL(valueChanged(int)), this, SLOT(slotSpinBox(int)));
}
void CWMiWidget::slotComboBox(int index) {
tb->setText(QString("Seleccionado el item %1 en el ComboBox").arg(index + 1));
}
void CWMiWidget::slotSpinBox(int num) {
tb->setText(QString("Seleccionado el item %1 en el SpinBox").arg(num));
}
En el código de este ejemplo no hay mucho que explicar pues ya algunas cosas las hemos visto; lo único nuevo interesante es el uso del DockArea. Este elemento nos permite ubicar componentes acoplables, como el caso de las barras de herramientas, en cualquier parte de nuestro widget personalizado. Si se arrastra la barra de herramientas fuera del área de acoplamiento, se observa como se convierte en una ventanita; si se hace doble clic sobre el título de la ventanita entonces se vuelve a acoplar al DockArea. Por medio de este elemento podemos ubicar la barra de herramientas en cualquier widget que lo necesite. Luego que creamos el DockArea, creamos la barra de herramientas pero teniendo como padre al DockArea. Esto tiene que ser así porque la clase QToolBar necesita saber el área donde será acoplado el objeto luego de ser creado. Ya teniendo la barra de herramientas vamos creando los objetos que queremos añadirle, poniéndoles como padre la barra de herramientas donde queremos que se inserten, en nuestro caso un KComboBox, un KIntSpinBox y tres QToolButton; una vez creados todos los componentes que van en la barra de herramientas, la añadimos al layout de nuestro widget. Es de notar que el orden de creación de los componentes altera el orden de visualización de los controles en la barra de herramientas. Por último, creamos las conexiones de los controles con los slots que recibirán las señales.
Nuestro archivo principal también hay que modificarlo pues ahora debemos instanciar un objeto de la clase CWMiWidget y asignarlo como objeto principal de nuestra aplicación.
Archivo kapp.cpp
#include "CWMiWidget.h"
#include <kapp.h>
int main( int argc, char **argv ) {
KApplication kaplicacion(argc, argv, "kapp_CWMiWidget");
CWMiWidget *mw = new CWMiWidget(0, "CWMiWidget");
kaplicacion.setMainWidget(mw);
return kaplicacion.exec();
}
El archivo de proyecto también debe ser modificado para incluir los archivos del objeto creado.
kapp.pro
# Opciones de compilación
CONFIG = qt
INCLUDEPATH = /usr/include/kde ./include
LIBS = -lkdeui -L/usr/lib/
TEMPLATE = app
TARGET = kaplicacion
DEPENDPATH = .
# Archivos con código fuente
HEADERS += include/CWMiWidget.h
SOURCES += src/CWMiWidget.cpp src/kapp.cpp
# Directorios de destino
DESTDIR = bin
MOC_DIR = moc
OBJECTS_DIR = obj
Una vez modificado se ejecuta en una cónsola:
qmake && make && bin/kaplicacion
Y tendremos nuestro programa funcionando y mostrando una apariencia similar a ésta:
Aunque los puristas de la programación orientada a objetos dicen que si un objeto no va a ser instanciado más de una vez, entonces no es necesario el uso de una clase para ese objeto, en cuanto al uso de widgets me parece más útil encapsularlos en una clase para poder reusarlo en otro momento. Usando este enfoque podemos disponer de nuestro widget en cualquier otra aplicación. Por ejemplo, en esta imagen se muestra una aplicación que hace uso de dos pestañas y en cada pestaña se usa nuestro widget.
Esto fue hecho simplemente modificando el archivo principal para demostrar el uso de nuestro widget personalizado. El archivo principal quedaría así:
Archivo kapp.cpp
#include "CWMiWidget.h"
#include <kapp.h>
#include <klocale.h>
#include <qtabwidget.h>
int main( int argc, char **argv ) {
KLocale::setMainCatalogue("kdelibs");
KApplication kaplicacion(argc, argv, "kapp_CWMiWidget");
QTabWidget *tab = new QTabWidget(0, "tab");
tab->addTab(new CWMiWidget(tab, "CWMiWidget_1"), "CWMiWidget #1");
tab->addTab(new CWMiWidget(tab, "CWMiWidget_2"), "CWMiWidget #2");
tab->show();
kaplicacion.setMainWidget(tab);
return kaplicacion.exec();
}