Arriba

Pronto volveremos

Estimados lectores, gracias por sus visitas. Nunca me imaginé que este blog tuviera la aceptación que ha tenido. Recibo visitas enlazadas desde páginas importantes del mundo linux, entre ellas kdehispano, ubuntu-es, etc., lo que me permite ver que hay una gran cantidad de desarrolladores buscando información para empezar a realizar aplicaciones para linux. Si bien los primeros meses nadie conocía este blog, en el último semestre las visitas pasan de 200, lo que para mí es más de lo esperado.

El blog sigue vivo, no lo he actualizado por cuestiones de tiempo. El blog empezará a hacer más énfasis en Qt que en KDE para que lo que se aprenda pueda ser usado en varias plataformas. He realizado algunas aplicaciones que manejan base de datos usando Qt 4, que si es estable, ya que ha sido lo que más me han solicitado. Solamente falta formatear los artículos para subirlos.

Claro que mientras más comentarios dejen más me apuraré en terminarlos :P.

Arriba

Creando Nuestro Propios Widgets

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 // Librerías Qt #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; // Funciones privadas void CrearToolbar(void); private slots: void slotComboBox (int index); void slotSpinBox (int num); }; #endif
Archivo CWMiWidget.cpp
#include "CWMiWidget.h" // Librerías KDE #include <kcombobox.h> #include <kiconloader.h> #include <knuminput.h> #include <ktextbrowser.h> // Librerías Qt #include <qdockarea.h> #include <qlayout.h> #include <qtoolbar.h> #include <qtoolbutton.h> /****************************************************************************** * Constructor del Widget. * ******************************************************************************/ 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(); } /****************************************************************************** * Crea y configura la barra de herramientas * ******************************************************************************/ void CWMiWidget::CrearToolbar(void) { // Creamos el DockArea y la barra de herramientas QDockArea *qda = new QDockArea(Qt::Horizontal, QDockArea::Normal, this, "CWMiWidget_qda"); QToolBar *qtb = new QToolBar ("ToolBar", NULL, qda, FALSE, "CWMiWidget_qtb"); // Creamos y añadimos los widgets a la barra de herramientas // Primero creamos el ComboBox cmb = new KComboBox(FALSE, qtb, "CWMiWidget_cmb"); // LLenamos con algo el ComboBox for (int i = 1; i < 11; i++) cmb->insertItem(QString::number(i)); // Ahora creamos el SpinBox sp = new KIntSpinBox(1, 10, 1, 1, 10, qtb, "CWMiWidget_sp"); // Creamos los tres botones y los añadimos a la barra de herramientas 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"); // Añadimos el DockArea con la barra de herramientas al Layout de CWMiWidget Layout->addWidget(qda, 0, 0); // Creamos las conexiones a los slots connect(cmb, SIGNAL(activated(int)), this, SLOT(slotComboBox(int))); connect(sp, SIGNAL(valueChanged(int)), this, SLOT(slotSpinBox(int))); } /****************************************************************************** * Cambia el texto según el item seleccionado en el combo box * ******************************************************************************/ void CWMiWidget::slotComboBox(int index) { tb->setText(QString("Seleccionado el item %1 en el ComboBox").arg(index + 1)); } /****************************************************************************** * Cambia el texto según el item seleccionado en el spin box * ******************************************************************************/ 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:

Creando Nuestro Propio Widget

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.

Creando Nuestro Propio Widget  Creando Nuestro Propio 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" // Encabezados KDE #include <kapp.h> #include <klocale.h> // Encabezados Qt #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(); }
Arriba

Creando Un StatusBar

Una barra de estado permite mostrar de manera disimulada información referente a nuestra aplicación. Si nuestro programa hace alguna actividad en segundo plano, el usuario puede ser notificado viendo la barra de estado; si el archivo ha sido modificado y debe ser guardado, la barra de estado puede indicarlo; en un editor de textos también puede mostrar información como la línea y la columna actual. En este artículo tomaremos el código desarrollado en Un Editor Sencillo De Documentos y le añadiremos la barra de estado para que muestre el nombre del archivo abierto por poner algo, y un widget de la clase KProgress que puede ser usado para mostrar el avance al cargar el archivo a editar (no se incluirá el código para esta función dejándoselo de tarea al lector); cualquier otra cosa la puede añadir el lector usando el mismo procedimiento que con la barra de progreso.

La barra de estado es manejada por la clase KStatusBar la cual corresponde a la biblioteca libkdeui. Si el widget principal de la aplicación es derivado de KMainWindow ya disponemos de una barra de estado automáticamente, a la cual tenemos acceso mediante el método statusbar() que devuelve un puntero a dicha barra perteneciente a la ventana principal. Si queremos usar otra barra u otro método, se debe ubicar de forma manual, por ejemplo usando un layout. En nuestro caso, como dijimos, tenemos un objeto principal derivado de KMainWindow, así que esta clase hará el trabajo de ubicarla por nosotros. El código de nuestra clase principal quedaría así:

Archivo KMiMainWindow.cpp
#include "KMiMainWindow.h" // Encabezados KDE #include <kaction.h> #include <kmenubar.h> #include <kpopupmenu.h> #include <kstdaction.h> #include <ktoolbar.h> #include <kfiledialog.h> #include <ktextedit.h> // Encabezados Qt #include <qfile.h> #include <qtextstream.h> // Encabezados añadidos #include <kprogress.h> #include <kstatusbar.h> /****************************************************************************** * Constructor de la clase. * ******************************************************************************/ KMiMainWindow::KMiMainWindow(QWidget *parent, const char *name) : KMainWindow(parent, name) { setGeometry(0, 0, 400, 400); setCaption("MiMainWindow"); CrearStatusBar(); CrearAcciones(); CrearMenu(); CrearToolbar(); txtEditor = new KTextEdit(this, "txtEditor"); setCentralWidget(txtEditor); } /****************************************************************************** * Procedimiento encargado de crear las acciones que usaremos en el programa * ******************************************************************************/ void KMiMainWindow::CrearAcciones(void) { actAbrir = KStdAction::open (this, SLOT(slotAbrir()), actionCollection()); actCopiar = KStdAction::copy (this, SLOT(slotCopiar()), actionCollection()); actCortar = KStdAction::cut (this, SLOT(slotCortar()), actionCollection()); actGuardar = KStdAction::save (this, SLOT(slotGuardar()), actionCollection()); actPegar = KStdAction::paste(this, SLOT(slotPegar()), actionCollection()); actSalir = KStdAction::quit (this, SLOT(slotSalir()), actionCollection()); actAbrir->setToolTip ("Abre un archivo en el editor"); actCopiar->setToolTip ("Copia el texto seleccionado al portapapeles"); actCortar->setToolTip ("Elimina el texto seleccionado y lo copia al portapapeles"); actGuardar->setToolTip("Guarda el texto actual en un archivo"); actPegar->setToolTip ("Pega el texto del portapapeles"); actSalir->setToolTip ("Termina el programa"); actionCollection()->setHighlightingEnabled(true); connect(actionCollection(), SIGNAL(actionStatusText(const QString &)), statusBar(), SLOT (message(const QString &))); connect(actionCollection(), SIGNAL(clearStatusText()), statusBar(), SLOT (clear())); } /****************************************************************************** * Procedimiento para crear el menú y añadir las acciones * ******************************************************************************/ void KMiMainWindow::CrearMenu(void) { mb = new KMenuBar(this, "MenuBar"); p = new KPopupMenu(this, "PopupArchivo"); actAbrir->plug(p); actGuardar->plug(p); p->insertSeparator(); actSalir->plug(p); mb->insertItem("&Archivo", p); p = new KPopupMenu(this, "PopupEdicion"); actCopiar->plug(p); actCortar->plug(p); actPegar->plug(p); mb->insertItem(QString::fromUtf8("&Edición"), p); } /****************************************************************************** * Procedimiento para crear la barra de herramientas y añadir las acciones. * ******************************************************************************/ void KMiMainWindow::CrearToolbar(void) { KToolBar *tb = new KToolBar(this, QMainWindow::Top, false, "Toolbar", true, false); actAbrir->plug(tb); actGuardar->plug(tb); tb->insertLineSeparator(); actCopiar->plug(tb); actCortar->plug(tb); actPegar->plug(tb); tb->insertLineSeparator(); actSalir->plug(tb); } /****************************************************************************** * Procedimiento para crear la barra de estado. * ******************************************************************************/ void KMiMainWindow::CrearStatusBar(void) { statusBar()->show(); pbar = new KProgress(this, "ProgressBar"); pbar->setFixedHeight(18); // Añadimos la etiqueta para indicar lo que hace el programa statusBar()->insertItem("Archivo en blanco", 0, 1); // Insertamos la barra de progreso en la barra de estado statusBar()->addWidget(pbar); statusBar()->setItemAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); } /****************************************************************************** * SLOT para procesar la acción Abrir * ******************************************************************************/ void KMiMainWindow::slotAbrir(void) { // Primero obtenemos el nombre del archivo a abrir. Archivo = KFileDialog::getOpenFileName(); QFile ArchEntr(Archivo); // Lo abrimos en modo lectura if (ArchEntr.open(IO_ReadOnly)) { // Lo leemos QTextStream ts(&ArchEntr); // Llenamos el txtEditor con el contenido del archivo txtEditor->setText(ts.read()); statusBar()->changeItem(Archivo, 0); setCaption("MiMainWindow - " + Archivo); } } /****************************************************************************** * SLOT para procesar la acción Copiar * ******************************************************************************/ void KMiMainWindow::slotCopiar(void) { txtEditor->copy(); } /****************************************************************************** * SLOT para procesar la acción Cortar * ******************************************************************************/ void KMiMainWindow::slotCortar(void) { txtEditor->cut(); } /****************************************************************************** * SLOT para procesar la acción Guardar * ******************************************************************************/ void KMiMainWindow::slotGuardar(void) { // Abrimos el archivo en modo escritura QFile ArchSal(Archivo); if (ArchSal.open(IO_WriteOnly)) { // Asignamos el stream de salida al archivo QTextStream ts(&ArchSal); // Guardamos en el archivo el contenido de txtEditor ts << txtEditor->text(); } } /****************************************************************************** * SLOT para procesar la acción Pegar * ******************************************************************************/ void KMiMainWindow::slotPegar(void) { txtEditor->paste(); } /****************************************************************************** * SLOT para procesar la acción Salir * ******************************************************************************/ void KMiMainWindow::slotSalir(void) { close(); }
Archivo KMiMainWindow.h
#include <kmainwindow.h> class KAction; class KMenuBar; class KPopupMenu; class KProgress; class KTextEdit; class QString; class KMiMainWindow : public KMainWindow { Q_OBJECT public: KMiMainWindow(QWidget *parent = 0, const char *name = 0); private: // Variables privadas KAction *actAbrir; KAction *actCopiar; KAction *actCortar; KAction *actGuardar; KAction *actPegar; KAction *actSalir; KMenuBar *mb; KPopupMenu *p; KProgress *pbar; KTextEdit *txtEditor; QString Archivo; // Funciones Privadas void CrearAcciones(void); void CrearMenu(void); void CrearStatusBar(void); void CrearToolbar(void); private slots: void slotAbrir(void); void slotCopiar(void); void slotCortar(void); void slotGuardar(void); void slotPegar(void); void slotSalir(void); };

Esta vez hemos añadido el método CrearStatusBar() para manejar lo referente a la inicialización de la barra de estado. Durante la creación de las acciones, se conectan las señales emitidas por los KAction a través de actionCollection() con los slots de la barra de estado. Con ésto logramos que cada vez que se seleccione un menú y se resalte un item, se muestre en la barra de estado el tooltip asociado con el item resaltado. A continuación los pantallazos de nuestra aplicación recién creada.

Creando Un StatusBar  Creando Un StatusBar

Podemos observar en el primero cómo se muestra en la barra de estado el tooltip referente a la acción seleccionada Abrir, mientras que el segundo muestra el tooltip como tal al posicionar el mouse sobre el botón de la barra de herramientas que corresponde con la acción Abrir. Esta vez hemos usado un nuevo método llamado QString::fromUtf8() el cual permite mostar un texto escrito en UTF-8 independientemente de la configuración del sistema donde se ejecute nuestro programa, a partir de ahora todo nuestro código será escrito en formato UTF-8 por lo que haremos uso de esta función constantemente.

Arriba

Uso Simple De Layouts

En el artículo anterior vimos como colocar componentes en una ventana de manera absoluta. A pesar de parecer tan fácil, tiene el inconveniente de la apariencia de los controles cuando se redimensiona la ventana. Los layouts vienen a suplir esta carencia, facilitándole al programador la labor de acomodar la apariencia sin tener que codificar algo. Lenguajes como Java hace uso intensivo de layouts pero programadores de otros lenguajes como Visual Basic o Delphi puede parecerle extraño o complicada la manera de colocar los objetos usando layouts; la primera vez que vi layouts fue haciendo un programa en Java usando AWT y terminé frustrado el primer día porque no se colocaban los controles como esperaba, luego de unos días de prácticas conseguí entender su uso y ahora es parte fundamental en mis diseños de interfaces usando Qt, porque Java... no gracias.

El toolkit Qt nos provee varios layouts para facilitar la ubicación de los componentes, incluso provee contenedores como los QHBox y QVBox que consumen menos recursos que los layouts y se emplean cuando no se requere mayor cosa al momento de colocar los componentes. De todos los layouts que provee Qt solo explicaré el QGridLayout, ya que al fin y al cabo se amolda a cualquier situación y puede ser usado para cualquier interfaz. Si el lector quiere, puede buscar en la documentación de Qt la ayuda de los otros layouts ya que todos derivan de la misma clase base QLayout.

El QGridLayout es semejante a usa hoja de cálculo pues está dividido en filas y columnas. La unión de cada fila y columna forman una celda y una celda puede abarcar varias columnas y/o filas si se unen. Cada objeto que es creado luego es añadido al layout indicándole la fila y columna a ocupar.

Para poder diseñar la apariencia se debe delimitar la ubicación de los controles a usar. Siguiendo el mismo ejemplo del artículo anterior, tenemos entoces diez botones donde cada botón podríamos considerar que ocupa una celda a excepción del "0" que ocupa tres columnas en la misma fila, por lo que necesitamos crear un QGridLayout de cuatro filas por tres columnas. Lo demás es simple: crear los objetos y añadirlos al layout indicando su posición. Nuestro código quedaría así:

Archivo KMiMainWindow.cpp
#include "KMiMainWindow.h" #include <kpushbutton.h> // Encabezados añadidos #include <qlayout.h> /****************************************************************************** * Constructor de la clase. * ******************************************************************************/ KMiMainWindow::KMiMainWindow(QWidget *parent, const char *name) : KMainWindow(parent, name) { KPushButton *pb; setCaption("MiMainWindow"); setGeometry(0, 0, 125, 165); // Los parámetros del constructor del layout son: // QGridLayout(pariente, filas, columnas, margen, espaciado, nombre) QGridLayout *gl = new QGridLayout(this, 4, 3, 5, 5, "layout"); // Ahora creamos los 10 botones y los ubicamos en el layout. // Los parámetros del constructor del botón son: // KPushButton(etiqueta que mostrará el botón, pariente, nombre) pb = new KPushButton("1", this, "1"); pb->setMinimumSize(35, 35); gl->addWidget(pb, 0, 0); pb = new KPushButton("2", this, "2"); pb->setMinimumSize(35, 35); gl->addWidget(pb, 0, 1); pb = new KPushButton("3", this, "3"); pb->setMinimumSize(35, 35); gl->addWidget(pb, 0, 2); pb = new KPushButton("4", this, "4"); pb->setMinimumSize(35, 35); gl->addWidget(pb, 1, 0); pb = new KPushButton("5", this, "5"); pb->setMinimumSize(35, 35); gl->addWidget(pb, 1, 1); pb = new KPushButton("6", this, "6"); pb->setMinimumSize(35, 35); gl->addWidget(pb, 1, 2); pb = new KPushButton("7", this, "7"); pb->setMinimumSize(35, 35); gl->addWidget(pb, 2, 0); pb = new KPushButton("8", this, "8"); pb->setMinimumSize(35, 35); gl->addWidget(pb, 2, 1); pb = new KPushButton("9", this, "9"); pb->setMinimumSize(35, 35); gl->addWidget(pb, 2, 2); pb = new KPushButton("0", this, "0"); pb->setMinimumSize(35, 35); gl->addMultiCellWidget(pb, 3, 3, 0, 2); }

En el constructor del QGridLayout le indicamos el número de filas y columnas que vamos a necesitar, en nuestro caso 4x3. También le indicamos dos parámetros que nos ayudarán en el uso de los layouts: el margen y el espaciado. El margen es la separación que hay entre en el elemento padre y el layout mientras que el espaciado es la separación entre los componentes colocados en el layout. La siguientes tablas ilustran los cuatro parámetros usados.

1 2 3
4 5 6
7 8 9
0
 
1   2   3
4   5   6
7   8   9
0

La línea azul indica donde se coloca el layout con respecto al objeto padre, que puede ser una ventana, la línea roja muestra el borde del layout y las líneas negras son los bordes de los controles. El ejemplo de la izquierda muestra un margen y un espaciado de 0 mientras que el segundo muestra un margen de 10 y un espaciado de 10. El margen viene siendo la separación entre la línea roja y la azul mientras que el espaciado es la separación entre las líneas negras de los controles. El ejemplo fue hecho usando tablas HTML así que la apariencia puede variar de un navegador a otro así como también puede parecer que la separación de las filas no es la misma que la de las columnas. Es recomendable ir cambiando los valores para ir notando como cambia el diseño, así se tendrá un mejor aprendizaje.

Cada vez que se crea un control lo añadimos al layout por medio del método addWidget y le pasamos el puntero del objeto a añadir así como la fila y la columna donde será ubicado. Si el control es multicolumna y/o multifila se usa el método addMultiCellWidget; este método acepta cuatro parámetros además del objeto padre: fila inicial, fila final, columna inicial y columna final. En el caso del "0", que se expande 3 columnas, indicamos 0 como la columna inicial y 2 como la columna final, mientras que los valores para las filas son las mismas ya que no hay expansión de filas, por eso ambos valores son 3.

Una vez compilado el programa y ejecutado obtenemos una ventana parecida a la del artículo anterior; la diferencia está al momento de redimensionar la ventana. Si redimensionamos la ventana obtenemos algo parecido a la siguiente imagen, donde observamos que los controles son redimensionados proporcionalmente para llenar toda la ventana; ésto es hecho automáticamente por el layout. Si queremos que el crecimiento no sea proporcional, podemos configurarlo cambiando los parámetros de stretch de cada columna y fila.

Uso simple de layouts

En la imagen se puede apreciar que el resultado obtenido es mucho más estético que en el caso de la colocación absoluta cuando se redimensiona. En realidad, una vez que se domina el uso de layouts difícilmente se vuelva a usar el modo absoluto para ubicar los controles en una ventana o control padre.

Arriba

Colocación De Componentes En Una Ventana

En los artículos pasados hemos visto cómo realizar una aplicación con un componente principal que ocupa toda la ventana, más especificamente un KTextEdit. Pero, ¿y si queremos colocar varios objetos en nuestra ventana principal?

Hay dos maneras de colocar los controles: de manera absoluta y usando layouts. En este artículo veremos la manera absoluta de colocarlos. Los programadores de lenguajes visuales como Delphi y Visual Basic estarán acostumbrados a la ubicación de esta manera de los controles en la ventana. Usando este método a cada control se le asignan cuatro valores: distancia desde el borde izquierdo, distancia desde el borde superior, ancho y alto. De esta manera queda definida la situación y dimensión geométrica del componente en la ventana.

Un buen ejemplo para demostrar el uso de este método sería la distribución de las teclas de un teléfono sencillo, sin incluir las teclas # y *. Usando el esqueleto creado en el artículo Esquema Básico Modificado De Una Aplicación KDE vamos a realizar la distribución de las teclas de manera absoluta. El código de nuestra aplicación quedaría ahora de la siguiente manera:

Archivo KMiMainWindow.cpp
#include "KMiMainWindow.h" // Encabezados añadidos #include <kpushbutton.h> /****************************************************************************** * Constructor de la clase. * ******************************************************************************/ KMiMainWindow::KMiMainWindow(QWidget *parent, const char *name) : KMainWindow(parent, name) { KPushButton *pb; setGeometry(0, 0, 125, 165); setCaption("MiMainWindow"); // Creamos 10 botones y los ubicamos en la ventana de manera absoluta. // El primer parámetro del constructor es la etiqueta que mostrará el botón. // El segundo es el pariente del widget. // El tercero es el nombre del widget creado. // Los parámetros de setGeometry son (x, y, ancho, alto) donde // x es la distancia desde el borde izquierdo // y es la distancia desde el borde superior // La coordenada (0,0) es contado luego de las barras de título, menú y herramientas pb = new KPushButton("1", this, "1"); pb->setGeometry( 5, 5, 35, 35); pb = new KPushButton("2", this, "2"); pb->setGeometry(45, 5, 35, 35); pb = new KPushButton("3", this, "3"); pb->setGeometry(85, 5, 35, 35); pb = new KPushButton("4", this, "4"); pb->setGeometry( 5, 45, 35, 35); pb = new KPushButton("5", this, "5"); pb->setGeometry(45, 45, 35, 35); pb = new KPushButton("6", this, "6"); pb->setGeometry(85, 45, 35, 35); pb = new KPushButton("7", this, "7"); pb->setGeometry( 5, 85, 35, 35); pb = new KPushButton("8", this, "8"); pb->setGeometry(45, 85, 35, 35); pb = new KPushButton("9", this, "9"); pb->setGeometry(85, 85, 35, 35); pb = new KPushButton("0", this, "0"); pb->setGeometry( 5, 125, 115, 35); }

Una vez compilado y ejecutado nuestro programa, presentará una interfaz parecida a la siguiente:

Colocación de componentes en una ventana

El problema que presenta este método es que si el usuario decide redimensionar la ventana, los controles permanecerán iguales, dando una mala apariencia. A continuación se puede ver como la ventana ha aumentado de tamaño pero los controles no se acomodan al nuevo tamaño de la ventana, afeando nuestra interfaz.

Colocación de componentes en una ventana

Para evitar esta situación se pueden optar por una de tres soluciones: colocar la ventana en modo no redimensionable, interceptar el evento de redimensionado y acomodar los controles durante este evento y el uso de layouts. En el próximo artículo veremos la opción más elegante: el uso de layouts.