jueves, 1 de agosto de 2013

Desarrollo de una Tabla caché, usando el Framework Qt.

clip_image002

En la presente entrada se explica la implementación, de una Tabla caché, que muestra una vista de tabla, se puede utilizar para acceder a una base de datos, y realiza el almacenamiento en caché de cualquier cambio en los datos hasta que el usuario haga clic en el botón “Submit”. Este ejemplo nos permitirá comprobar las potencialidades del Framework Qt, para el trabajo con Bases de Datos y el lenguaje SQL [1].

clip_image003

La implementación consta de una sola clase, TableEditor, que es un widget diálogo personalizado que permite al usuario modificar los datos almacenados en una base de datos. Primero vamos a revisar la definición de la clase y luego vamos a echar un vistazo a la implementación.

La definición de la clase TableEditor

La clase TableEditor hereda QDialog haciendo el widget editor de tablas de una ventana de diálogo de alto nivel.

class TableEditor : public QWidget
{
Q_OBJECT

public:
explicit TableEditor(const QString &tableName, QWidget *parent = 0);

private slots:
void submit();

private:
QPushButton *submitButton;
QPushButton *revertButton;
QPushButton *quitButton;
QDialogButtonBox *buttonBox;
QSqlTableModel *model;
};

El constructor TableEditor toma dos argumentos: el primero es un puntero al widget padre y se pasa al constructor de la clase base. El otro argumento es una referencia a la tabla de base de datos donde operará el objeto TableEditor.

Tenga en cuenta la declaración de la variable QSqlTableModel: Como veremos en este ejemplo, la clase QSqlTableModel puede ser utilizada para proporcionar datos que pueden ser visualizados por la clase QTableView. La clase QSqlTableModel proporciona un modelo de datos editable por lo que es posible leer y escribir los registros de base de datos de una sola tabla. Está construido en la parte superior de la clase de nivel inferior QSqlQuery que proporciona los medios para la ejecución y manipulación de sentencias SQL.

También vamos a mostrar como una vista de tabla se puede utilizar para almacenar en caché los cambios a los datos, hasta que el usuario solicita expresamente que los presente. Por eso tenemos que declarar un slot denominado show () que sirva al modelo y a los botones del editor.

Conexión a una base de datos

Antes de poder utilizar la clase TableEditor, tenemos que crear una conexión con la base de datos que contiene la tabla que queremos modificar:

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
if (!createConnection())
return 1;

TableEditor editor("person");
editor.show();
return app.exec();
}

La función CreateConnection () es una función auxiliar para comodidad del lector, que se define en el archivo connection.h. Dicha función es utilizada para conectarse a una base de datos.

static bool createConnection()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if (!db.open()) {
QMessageBox::critical(0, qApp->tr("Cannot open database"),
qApp->tr("Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information how "
"to build it.\n\n"
"Click Cancel to exit."), QMessageBox::Cancel);
return false;
}

QSqlQuery query;
query.exec("create table person (id int primary key, "
"firstname varchar(20), lastname varchar(20))");
query.exec("insert into person values(101, 'Danny', 'Young')");
query.exec("insert into person values(102, 'Christine', 'Holand')");
query.exec("insert into person values(103, 'Lars', 'Gordon')");
query.exec("insert into person values(104, 'Roberto', 'Robitaille')");
query.exec("insert into person values(105, 'Maria', 'Papadopoulos')");

query.exec("create table offices (id int primary key,"
"imagefile int,"
"location varchar(20),"
"country varchar(20),"
"description varchar(100))");
query.exec("insert into offices "
"values(0, 0, 'Oslo', 'Norway',"
"'Oslo is home to more than 500 000 citizens and has a "
"lot to offer.It has been called \"The city with the big "
"heart\" and this is a nickname we are happy to live up to.')");
query.exec("insert into offices "
"values(1, 1, 'Brisbane', 'Australia',"
"'Brisbane is the capital of Queensland, the Sunshine State, "
"where it is beautiful one day, perfect the next. "
"Brisbane is Australia''s 3rd largest city, being home "
"to almost 2 million people.')");
query.exec("insert into offices "
"values(2, 2, 'Redwood City', 'US',"
"'You find Redwood City in the heart of the Bay Area "
"just north of Silicon Valley. The largest nearby city is "
"San Jose which is the third largest city in California "
"and the 10th largest in the US.')");
query.exec("insert into offices "
"values(3, 3, 'Berlin', 'Germany',"
"'Berlin, the capital of Germany is dynamic, cosmopolitan "
"and creative, allowing for every kind of lifestyle. "
"East meets West in the metropolis at the heart of a "
"changing Europe.')");
query.exec("insert into offices "
"values(4, 4, 'Munich', 'Germany',"
"'Several technology companies are represented in Munich, "
"and the city is often called the \"Bavarian Silicon Valley\". "
"The exciting city is also filled with culture, "
"art and music. ')");
query.exec("insert into offices "
"values(5, 5, 'Beijing', 'China',"
"'Beijing as a capital city has more than 3000 years of "
"history. Today the city counts 12 million citizens, and "
"is the political, economic and cultural centre of China.')");

query.exec("create table images (locationid int, file varchar(20))");
query.exec("insert into images values(0, 'images/oslo.png')");
query.exec("insert into images values(1, 'images/brisbane.png')");
query.exec("insert into images values(2, 'images/redwood.png')");
query.exec("insert into images values(3, 'images/berlin.png')");
query.exec("insert into images values(4, 'images/munich.png')");
query.exec("insert into images values(5, 'images/beijing.png')");

return true;
}

La función CreateConnection abre una conexión a una base de datos SQLITE en memoria y crea una tabla de prueba. Si desea utilizar otra base de datos, sólo tiene que modificar el código de esta función.

Clase de implementación TableEditor

La implementación de la clase se compone de sólo dos funciones, el constructor y el “slot” show (). En el constructor creamos y personalizamos el modelo de datos y los distintos elementos de la ventana:

TableEditor::TableEditor(const QString &tableName, QWidget *parent)
: QWidget(parent)
{
model = new QSqlTableModel(this);
model->setTable(tableName);
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();

model->setHeaderData(0, Qt::Horizontal, tr("ID"));
model->setHeaderData(1, Qt::Horizontal, tr("First name"));
model->setHeaderData(2, Qt::Horizontal, tr("Last name"));

En primer lugar vamos a crear el modelo de datos y establecer la tabla de base de datos SQL, con la cual queremos que el modelo funcione. Tenga en cuenta que la función QSqlTableModel :: setTable () no selecciona datos de la tabla, sino que sólo obtiene su información de campo. Por eso llamamos más tarde a la función QSqlTableModel :: select (), poblando el modelo con los datos de la tabla. La selección se puede personalizar mediante la especificación de los filtros y las condiciones de ordenación.

También establecemos la estrategia de edición del modelo. La estrategia de edición dicta cuando los cambios realizados por el usuario en la vista, en realidad se aplican a la base de datos. Dado que queremos almacenar en caché los cambios en la vista de tabla (es decir, en el modelo) hasta que el usuario los decida enviar expresamente, elegimos la estrategia QSqlTableModel::OnManualSubmit. Las alternativas son QSqlTableModel :: OnFieldChange y QSqlTableModel :: OnRowChange.

Por último, hemos creado las etiquetas que aparecen en la cabecera de vista con la función setHeaderData () que el modelo hereda de la clase QSqlQueryModel.

QTableView *view = new QTableView;
view->setModel(model);
view->resizeColumnsToContents();

Luego creamos una vista de tabla. La clase QTableView proporciona una implementación de modelo / vista predeterminada de una vista de tabla, es decir, se aplica una vista de tabla que muestra elementos de un modelo. También permite al usuario editar los artículos, el almacenamiento de los cambios en el modelo. Para crear una vista de sólo lectura, establezca el indicador adecuado mediante la propiedad editTriggers la vista hereda de la clase QAbstractItemView.

Para que la vista presente nuestros datos, pasamos nuestro modelo a la vista mediante la función setModel ().

    submitButton = new QPushButton(tr("Submit"));
submitButton->setDefault(true);
revertButton = new QPushButton(tr("&Revert"));
quitButton = new QPushButton(tr("Quit"));

buttonBox = new QDialogButtonBox(Qt::Vertical);
buttonBox->addButton(submitButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(revertButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);

Los botones de la interfaz TableEditor son objetos QPushButton regulares. Se le añade una caja de botones para asegurar de que los botones se presentan en un diseño que sea adecuado para el estilo del widget actual. La razón de esto es que los cuadros de diálogo y cuadros de mensaje normalmente presentan botones en una disposición que se ajusta a las directrices de la interfaz para la plataforma. Invariablemente, diferentes plataformas tienen diferentes diseños para sus diálogos. QDialogButtonBox permite a los desarrolladores agregar botones a la misma y se utilizará automáticamente el diseño adecuado para el entorno de escritorio del usuario.

La mayoría de los botones para un diálogo siguen ciertos roles. Al agregar un botón a una caja de botones con la función addButton (), la función del botón debe ser especificado usando la enumeración QDialogButtonBox :: ButtonRole. Alternativamente, QDialogButtonBox proporciona varios botones estándar (por ejemplo, Aceptar, Cancelar, Guardar) que se pueden utilizar.

    connect(submitButton, SIGNAL(clicked()), this, SLOT(submit()));
connect(revertButton, SIGNAL(clicked()), model, SLOT(revertAll()));
connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));

Conectamos el botón Quit para con el “slot” close () que permite cerrar el editor de tablas, y el botón de envío (Submit) a la “slot” show (). Este último slot se hará cargo de las transacciones de datos. Por último, conectamos el botón Revert con el slot RevertAll () de nuestro modelo, que permite revertir todos los cambios pendientes (es decir, la restauración de los datos originales).

QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(view);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);

setWindowTitle(tr("Cached Table"));
}

Al final se añade la caja de botones de la vista de tabla a un diseño, se instala la distribución en el widget editor de tablas, y se establece el título de la ventana del editor.

void TableEditor::submit()
{
model->database().transaction();
if (model->submitAll()) {
model->database().commit();
} else {
model->database().rollback();
QMessageBox::warning(this, tr("Cached Table"),
tr("The database reported an error: %1")
.arg(model->lastError().text()));
}
}

El slot show () se llama cada vez que los usuarios hacen clic en el botón Submit para guardar los cambios.

En primer lugar, comenzamos una transacción en la base de datos utilizando la función transaction () de QSqlDatabase. Una transacción de base de datos es una unidad de interacción con un sistema de gestión de base de datos o sistema similar que se trata de una manera coherente y fiable independiente de otras transacciones. Un puntero a la base de datos utilizada puede ser obtenido usando la función QSqlTableModel ().

Entonces, tratamos de presentar todos los cambios pendientes, es decir, los elementos modificados del modelo. Si no se produce ningún error, nos comprometemos la transacción a la base de datos utilizando la función (tenga en cuenta que en algunas bases de datos, esta función no funcionará si hay un QSqlQuery activo en la base de datos) QSqlDatabase :: commit (). En caso contrario se realiza un rollback de la transacción utilizando la función QSqlDatabase :: rollback () y publicaremos un aviso al usuario.

El código fuente de esta aplicación pueden descargarlo desde aquí.



¿Te ha gustado este Post? Compártelo con tus amigos.

No hay comentarios:

Publicar un comentario

IconIconIcon