Tutorial de Qt 4: Item/View Widgets, mostrar conjuntos de datos de manera sencilla (Parte I)
En la parte anterior de este tutorial vimos como utilizar los diálogos estándar que Qt nos ofrece para realizar tareas comunes de interacción con el usuario, como solicitar confirmación para realizar acciones o informar del estado de una acción.
En este artículo revisaremos las características principales de las clases de conveniencia del framework “Model/View Programming” que nos ofrece Qt y revisaremos a detalle uno de los Item/View Widgets, el QListWidget.
Los widgets que revisaremos en este artículo forman parte de un framework dentro de Qt conocido como Item/View Programming. Este framework nos sirve para mostrar al usuario conjuntos de datos, conocidos como modelos de datos, que provengan de diversas fuentes como archivos de texto, bases de datos o la web. Dicho framework provee una arquitectura Modelo/Vista la cual es una ligera variación del patrón de diseño Modelo Vista Controlador (MVC) el cuál es un patrón muy popular en aplicaciones web, o en otras aplicaciones, generalmente grandes o complejas. Este patrón ofrece importantes beneficios en cuanto a rendimiento y mantenibilidad de la aplicación pero implementarlo tiene cierta complejidad para la cual probablemente no valga la pena atravesar en el caso de una aplicación sencilla o pequeña. Para casos como este, Qt provee los widgets “de conveniencia” que veremos en este artículo.
Comenzaremos por revisar el QLisWidgetItem, el cual es un widget que nos permite mostrar una un conjunto elementos en forma de lista y nos ofrece métodos para insertar y eliminar dichos elementos.
Usaremos una pequeña aplicación pare ejemplificar el uso de este widget, esta aplicación será una lista de compras o tareas, en la que podemos agregar, quitar y marcar productos como comprados. Cada que agreguemos un elemento a la lista, lo colocaremos en la parte superior de ella, dispondremos de una checkbox en cada elemento para indicar si ya fue comprado/completado o no, al marcar esta checkbox moveremos este producto al final de la lista y lo coloreamos de gris con el fin de que la atención se centre en los elementos restantes. Si nos equivocamos al agregar un elemento o decidimos que ya no lo queremos en la lista, simplemente lo seleccionamos y presionamos el botón eliminar.
Nuestro proyecto se compone de tres archivos:
- Lista.h Contiene la definición de la clase que representa al widget de nuestra lista
- Lista.cpp Contiene la implementación de la clase que representa al widget de nuestra lista
- main.cpp
A continuación mostramos y explicamos el código de los archivos que componen nuestra aplicación
NOTA: Sólo explicaremos el código relevante al tema de este tutorial o que no haya sido explicado antes en este tutorial.
El código del archivo ListaDeCompras.h es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #ifndef LISTADECOMPRAS_H #define LISTADECOMPRAS_H #include <QWidget> #include <QObject> class QListWidget; class QPushButton; class QListWidgetItem; class ListaDeCompras : public QWidget { Q_OBJECT public: ListaDeCompras(); private: QListWidget* lista; QPushButton* botonAgregar; QPushButton* botonQuitar; private slots: void botonAgregarPresionado(); void botonQuitarPresionado(); void productoMarcado(QListWidgetItem *); }; #endif // LISTADECOMPRAS_H |
De las líneas 4 a 9 incluimos los archivos de cabecera y definiciones de clases que utilizaremos en este ejemplo.
En la línea 11 comienza la definición de nuestra clase, de las líneas 18 a 20 declaramos los widgets que utilizaremos, en este caso sólo utilizaremos la lista y dos botones que nos permitan agregar y quitar elementos, respectivamente.
De las líneas 22 a 24 declaramos los slots que proveerán la funcionalidad de nuestra aplicación. Definimos un slot que conectamos a la señal emitida por cada botón y uno que se conecta a la señal que emite la lista cuando modificamos un elemento de ella, en este caso cuando marcamos o desmarcamos la casilla que indica que el elemento ha sido comprado/completado.
El código del archivo ListaDeCompras.cpp es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | #include "listadecompras.h" #include <QListWidget> #include <QListWidgetItem> #include <QPushButton> #include <QInputDialog> #include <QMessageBox> #include <QHBoxLayout> #include <QVBoxLayout> ListaDeCompras::ListaDeCompras() { QHBoxLayout* layoutPrincipal = new QHBoxLayout; QVBoxLayout* layoutBotones = new QVBoxLayout; lista = new QListWidget; botonAgregar = new QPushButton(trUtf8("Agregar")); botonQuitar = new QPushButton(trUtf8("Quitar")); layoutBotones->addWidget(botonAgregar); layoutBotones->addWidget(botonQuitar); layoutBotones->addStretch(); layoutPrincipal->addWidget(lista); layoutPrincipal->addLayout(layoutBotones); setLayout(layoutPrincipal); connect(botonAgregar, SIGNAL(clicked()), this, SLOT(botonAgregarPresionado())); connect(botonQuitar, SIGNAL(clicked()), this, SLOT(botonQuitarPresionado())); connect(lista, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(productoMarcado(QListWidgetItem*))); } void ListaDeCompras::botonAgregarPresionado() { QString nombreNuevoProducto = QInputDialog::getText(this, trUtf8("Lista de Compras"), trUtf8("Producto:")); if(nombreNuevoProducto.trimmed() == "") { QMessageBox::warning(this, trUtf8("Lista de Compras"), trUtf8("Debe indicar el nombre del producto")); return; } for(int i=0; i<lista->count(); i++) { if(nombreNuevoProducto == lista->item(i)->text()) { QMessageBox::warning(this, trUtf8("Lista de Compras"), trUtf8("Ya existe un producto con ese nombre")); return; } } QListWidgetItem* nuevoProducto = new QListWidgetItem(nombreNuevoProducto); nuevoProducto->setCheckState(Qt::Unchecked); lista->insertItem(0, nuevoProducto); } void ListaDeCompras::botonQuitarPresionado() { if(!lista->currentItem()) { QMessageBox::warning(this, trUtf8("Lista de Compras"), trUtf8("Ningún producto ha sido seleccionado")); return; } int respuesta = QMessageBox::question(this, trUtf8("Lista de Compras"), trUtf8("¿Desea borrar el producto %1?").arg(lista->currentItem()->text()), QMessageBox::Yes|QMessageBox::No); if(respuesta == QMessageBox::Yes) { delete lista->currentItem(); } } void ListaDeCompras::productoMarcado(QListWidgetItem* producto) { if(producto->checkState() == Qt::Checked) { lista->takeItem(lista->currentRow()); lista->insertItem(lista->count(), producto); lista->setCurrentRow(lista->count()-1); lista->currentItem()->setBackground(QColor("gray")); } else if(producto->checkState() == Qt::Unchecked) { lista->takeItem(lista->currentRow()); lista->insertItem(0, producto); lista->setCurrentRow(0); lista->currentItem()->setBackground(QColor("white")); } } |
De las líneas 1 a 8 incluimos los archivos de cabecera necesarios para el ejemplo.
De las líneas 12 a 17 creamos los componentes que utilizaremos en el ejemplo: Layouts para organizar la interfaz gráfica y los widgets declarados en ListaDeCompras.cpp
De las líneas 19 a 26 configuramos la interfaz gráfica, agregamos los botones a un layout vertical y agregamos también un stretch a este layout con el fin de mantener los botones en la parte superior del widget y agregamos la lista y el layout de los botones al layout principal de nuestra aplicación.
De las líneas 28 a 30 conectamos las señales que emiten los widgets de la interfaz a los slots que proveen la funcionalidad.
De la línea 33 a la 52 implementamos el slot botonAgregarPresionado, que como lo indica en el nombre se ejecuta cuando se presiona el botón agregar.
En la línea 35 creamos una cadena que recibe el nombre del nuevo producto/tarea a través de un InputDialog.
De la línea 37 a la 40 verificamos que se haya capturado el nombre del nuevo elemento. De la línea 42 a 47 verificamos que no exista un elemento con el mismo nombre. Si no se cumpla alguna de las dos condiciones mostramos un mensaje de aviso para informar porque el elemento no fue agregado a la lista y salimos de esta función.
En la línea 49 creamos un nuevo elemento de la lista el cual será un objeto de la clase QListWidgetItem, esto lo hacemos mediante el constructor
QListWidgetItem ( const QString & text, QListWidget * parent = 0, int type = Type )
Este construcción recibe un parámetro obligatorio y dos opcionales, como parámetro obligatorio está el texto del elemento y como opcionales están el widget padre del elemento, que debe ser un QListWidget, y un entero que representa el tipo de elemento. Si especificamos el padre del QListWidgetItem este se agrega de forma automática a esta lista.
En la línea 50 establecemos el estado de la marca del elemento. Si no especificamos el estado de la marca, no se mostrará la checkbox que lo indica, debido a que queremos mostrar esta checkbox especificamos el estado aunque el elemento no este marcado en un principio.
En la línea 51 agregamos el elemento a nuestra lista mediante la función
void QListWidget::insertItem ( int row, QListWidgetItem * item )
Esta función recibe como parámetros el índice de la fila en el que queremos insertar el elemento y un apuntador al elemento que queremos agregar a la lista. Si ya hay un elemento en esta posición los demás elementos se recorren un lugar adelante en la lista para dejar un lugar al nuevo elemento.
De las líneas 54 a 66 implementamos el slot botonQuitarPresionado, el cual se ejecuta cuando se presiona el botón quitar.
De las líneas 56 a 59 verificamos que algún elemento de la línea este seleccionado, si no hay ningún elemento seleccionado entonces mostramos un mensaje que informe de esto al usuario y salimos de la función. En caso de que sí haya un elemento seleccionado mostramos un diálogo para pedir al usuario que confirme si desea borrar el elemento. Si presiona el botón que indica que desea borrar el elemento entonces borramos el elemento de la lista con la instrucción delete y esté será destruido, Qt detectará esto y automáticamente removerá el elemento de la lista.
De la línea 68 a 82 implementamos el slot productoMarcado, el cual se ejecuta cuándo marcamos o desmarcamos la checkbox de cada elemento.
En la línea 70 comprobamos si el estado del elemento que la lista nos indica que fue modificado cambió a marcado o desmarcado. Si el estado cambió a marcado entonces movemos el elemento al final de la lista, esto lo logramos con las líneas
71 72 | lista->takeItem(lista->currentRow()); lista->insertItem(lista->count(), producto); |
Debido a que no existe una función que nos permita mover un elemento de posición lo que hacemos es quitar el elemento e insertarlo en la nueva posición que deseamos.
En la línea 71 quitamos el elemento de la lista me diante la función takeItem, la cual recibe como parámetro el índice de la fila en la que se encuentra el item que queremos quitar. Luego en la línea 72 insertamos nuevamente el elemento mediante la función insertItem.
En la línea 73 actualizamos el elemento que está seleccionado en la lista para que apunte nuevamente al elemento que nos interesa y que dejó de estar seleccionado debido a que lo quitamos de la lista. En la línea 74 cambiamos el color del fondo del elemento mediante la función setBackground color, la cual recibe como parámetro un objeto QColor.
En la línea 76 comienza la serie de acciones que se ejecutarán si el estado del elemento cambia a desmarcado. Estas acciones son muy similares a las que realizamos si el elemento cambia su estado a marcado. El cambio de posición lo realizamos mediante las mismas dos funciones, takeItem, insertItem y setCurrentRow, lo único que cambia es la posición en la que lo insertaremos, antes era al final, ahora es al principio. Y finalmente cambiamos el color de fondo del elemento nuevamente a blanco.
Esto es todo en este artículo en la siguiente parte de este tutorial revisaremos los otros dos Item/View Widgets: QTableWidget y QTreeWidget.
Artículos relacionados
- Tutorial de Qt 4: Signals y Slots, implementando la funcionalidad de la aplicación
- Tutorial de Qt 4: QMainWindow, la ventana principal de la aplicación
- Tutorial Gtk+2: Widgets, Signals y Callbacks (o "como funcionan las cosas")
- Tutorial de Qt4: Diálogos, comunicación con el usuario (Parte I)
- Tutorial de Qt 4: Introducción a Qt





Muro de Comentarios RSS Feed