Qt/C++ - Lesson 004. QSqlTableModel – How to present the table from database?

database, QSqlTableModel, QSqlTableModel example, qt, qt таблицы, sql, sqlite

In order to present the information in a database table we can to use the following classes:

  • QSqlQueryModel - model that generates a table by defining the raw SQL-query. It may be useful in the formation of a special custom filters and compile information from various database tables. About it in more detail in later lessons.
  • QSqlTableModel - the subject of our discussion in this article. The model that generates the table from database using name of table. But we can to create table without information from other tables.
  • QSqlRelationalTableModel - a class that allows you to create a table with constraints from other tables, substituting the values of the table, which is the model on contained in other tables record ID.

For comfortable work with information that is placed in the database applied additional class, which partially constitutes a "facade" design pattern.

Project structure for QSqlTableModel

he project is created as an application Qt Widgets, and it contains the following files:

  • DataBase.pro - project profile;
  • mainwindow.h - header file of mainwindow;
  • mainwindow.cpp - source file of mainwindow;
  • main.cpp - file with main function;
  • mainwindow.ui - interface of mainwindow;
  • database.h - header file of helper class to be used for information that is placed in a database;
  • database.cpp - source file of helper class to be used for information that is placed in a database;

mainwindow.ui

We create a form of test application, which will be used from GUI QTableView object called tableView.

DataBase.pro

The profile of the project need to add a directive that declares the use of libraries of SQL.

#-------------------------------------------------
#
# Project created by QtCreator 2015-08-10T16:08:24
#
#-------------------------------------------------

QT       += core
QT       += gui
QT       += sql

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = DataBase
TEMPLATE = app


SOURCES += main.cpp\
        mainwindow.cpp \
    database.cpp

HEADERS  += mainwindow.h \
    database.h

FORMS    += mainwindow.ui

main.cpp

The file used in the project, being created by default.

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

This header file is placed QSqlTableModel object and the DataBase, an helper class used to work with the information that is placed in a database. In addition to these methods are declared to initialize the appearance and model QSqlTableModel our application.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSqlTableModel>
#include "database.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow  *ui;
    /* The project uses objects to interact with information in a 
     * database and model representation database table
     * */
    DataBase        *db;
    QSqlTableModel  *model;

private:
    /* Also present are two methods that form 
     * the model and appearance TableView
     * */
    void setupModel(const QString &tableName, const QStringList &headers);
    void createUI();
};

#endif // MAINWINDOW_H

mainwindow.cpp

The source file, which happens all the main action on the interaction with the data representation model.

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    /* The first step is to create an object that will be used 
     * to work with our database of data and initialize the database connection
     * */
    db = new DataBase();
    db->connectToDataBase();

    /* After that is done filling the database 
     * tables of content that will appear in the TableView
     * */
    for(int i = 0; i < 4; i++){
        QVariantList data;
        int random = qrand(); // Get random integers to be inserted into the database
        data.append(QDate::currentDate()); // Get the current date to be inserted into the database
        data.append(QTime::currentTime()); // Get the current time to be inserted into the database
        // Prepare the received random number to be inserted into the database
        data.append(random);           
        // Prepare message for insertion into the database
        data.append("Получено сообщение от " + QString::number(random)); 
        // Insert data into the database
        db->inserIntoTable(data);
    }

    /* Initialize the model to represent the data 
     * indicating the names of the columns
     * */
    this->setupModel(TABLE,
                     QStringList() << trUtf8("id")
                                   << trUtf8("Дата")
                                   << trUtf8("Время")
                                   << trUtf8("Рандомное число")
                                   << trUtf8("Сообщение")
               );

    /* Initialize the appearance of a table with data
     * */
    this->createUI();
}

MainWindow::~MainWindow()
{
    delete ui;
}

/* Method for initializing data representation model
 * */
void MainWindow::setupModel(const QString &tableName, const QStringList &headers)
{
    /* Initializes the data model representation with the installation name 
     * in the database table, on which will be accessed in the table
     * */
    model = new QSqlTableModel(this);
    model->setTable(tableName);

    /* Set the columns names in a table with sorted data
     * */
    for(int i = 0, j = 0; i < model->columnCount(); i++, j++){
        model->setHeaderData(i,Qt::Horizontal,headers[j]);
    }
    // Set Sort Ascending steering column data
    model->setSort(0,Qt::AscendingOrder);
}

void MainWindow::createUI()
{
    ui->tableView->setModel(model);     // We set the model on the TableView
    ui->tableView->setColumnHidden(0, true);    // Hide the column id Records
    // Allow the selection of lines
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    // Set selection mode, only one row in table
    ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
    // Set the size of the columns by content
    ui->tableView->resizeColumnsToContents();
    ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableView->horizontalHeader()->setStretchLastSection(true);

    model->select(); // Fetches the data from the table
}

database.h

In order not to be left in total darkness a question on the interaction with the database, also bring listings in this class with comments. The work with SQLite database, but the principle of working with network database will be similar, with the exception of the network connection and the nuances of each particular database.

The DataBase class header file, you must specify the names of the directive table and table columns for the convenience of further work. As well as the methods that are used to interact with the database, as well as provide an interface for this interaction with higher-level application layers.

#ifndef DATABASE_H
#define DATABASE_H

#include <QObject>
#include <QSql>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlDatabase>
#include <QFile>
#include <QDate>
#include <QDebug>

/* Directive table names, fields, tables, and databases */
#define DATABASE_HOSTNAME   "ExampleDataBase"
#define DATABASE_NAME       "DataBase.db"

#define TABLE                   "TableExample"
#define TABLE_DATE              "Date"
#define TABLE_TIME              "Time"
#define TABLE_MESSAGE           "Message"
#define TABLE_RANDOM            "Random"

class DataBase : public QObject
{
    Q_OBJECT
public:
    explicit DataBase(QObject *parent = 0);
    ~DataBase();
    /* Methods for direct work with the class to connect 
     * to a database and insert records into the table
     * */
    void connectToDataBase();
    bool inserIntoTable(const QVariantList &data);

private:
    // The very object database, which will be work
    QSqlDatabase    db;

private:
    /* Internal methods for working with database
     * */
    bool openDataBase();
    bool restoreDataBase();
    void closeDataBase();
    bool createTable();
};

#endif // DATABASE_H

database.cpp

The source helper class file to be used for information that is placed in a database. In this case, we will hide the SQL query in this class.

#include "database.h"

DataBase::DataBase(QObject *parent) : QObject(parent)
{

}

DataBase::~DataBase()
{

}

/* Methods for connecting to the database
 * */
void DataBase::connectToDataBase()
{
    /* Before connecting to a database checks for its existence. 
     * Depending on the result of the opening of manufacture database or its recovery
     * */
    if(!QFile("C:/example/" DATABASE_NAME).exists()){
        this->restoreDataBase();
    } else {
        this->openDataBase();
    }
}

/* Methods for restoring the database
 * */
bool DataBase::restoreDataBase()
{
    if(this->openDataBase()){
        if(!this->createTable()){
            return false;
        } else {
            return true;
        }
    } else {
        qDebug() << "Failed to restore the database";
        return false;
    }
    return false;
}

/* The method to open the database
 * */
bool DataBase::openDataBase()
{
    /* Database opens along a predetermined path and the database name, if it exists
     * */
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setHostName(DATABASE_HOSTNAME);
    db.setDatabaseName("C:/example/" DATABASE_NAME);
    if(db.open()){
        return true;
    } else {
        return false;
    }
}

/* Method for closing database
 * */
void DataBase::closeDataBase()
{
    db.close();
}

/* The method for creating a database table
 * */
bool DataBase::createTable()
{
    /* In this case, a forming raw SQL-query with its subsequent execution.
     * */
    QSqlQuery query;
    if(!query.exec( "CREATE TABLE " TABLE " ("
                            "id INTEGER PRIMARY KEY AUTOINCREMENT, "
                            TABLE_DATE      " DATE            NOT NULL,"
                            TABLE_TIME      " TIME            NOT NULL,"
                            TABLE_RANDOM    " INTEGER         NOT NULL,"
                            TABLE_MESSAGE   " VARCHAR(255)    NOT NULL"
                        " )"
                    )){
        qDebug() << "DataBase: error of create " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

/* The method to insert records into the database
 * */
bool DataBase::inserIntoTable(const QVariantList &data)
{
    /* SQL Query formed from QVariantList, 
     * which are transmitted in data to be inserted into the table.
     * */
    QSqlQuery query;
    /* e SQL query is generated beginning with keys, 
     * which then bind with bindValue method for substituting data from QVariantList
     * */
    query.prepare("INSERT INTO " TABLE " ( " TABLE_DATE ", "
                                             TABLE_TIME ", "
                                             TABLE_RANDOM ", "
                                             TABLE_MESSAGE " ) "
                  "VALUES (:Date, :Time, :Random, :Message )");
    query.bindValue(":Date",        data[0].toDate());
    query.bindValue(":Time",        data[1].toTime());
    query.bindValue(":Random",      data[2].toInt());
    query.bindValue(":Message",     data[3].toString());
    if(!query.exec()){
        qDebug() << "error insert into " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

Result

As a result, should get a table with the following type of data that have been prepared and inserted into the constructor of the main application window.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
Support the author Donate
I

Здравствуйте, а, скажите, сам объект QSqlTableModel предоставляет доступ к данным, которые уже есть в таблице? Например, получить в какую - то переменную ранее записанное рандомное число из строки с индексом n?Или, это только возможно запросом

I

Метод setData(строка, столбец, значение) есть, а, метода, например, типа, getData(строка, столбец), не нашел

I

Разобрался QSqlRecord rec = model(номер строки) - получаем строку int num = rec.value("TABLE_RANDOM"); - получаем поле

Всё гораздо проще, метод, который Вы искали называется не getData() , а просто data() . Для получения данных нужно дернуть QModelIndex из модели. Выглядеть это будет так:

model->data(model->index(0, 2)); 
// 0 - номер строки
// 2 - номер колонки

Метод вернёт QVariant , который будет содержать данные.

z

А у меня выхлоп: Не удалось восстановить базу данных QSqlQuery::prepare: database not open error insert into TableExample "No query Unable to fetch row" QSqlQuery::prepare: database not open error insert into TableExample "No query Unable to fetch row" QSqlQuery::prepare: database not open error insert into TableExample "No query Unable to fetch row" QSqlQuery::prepare: database not open error insert into TableExample "No query Unable to fetch row" QSqlQuery::prepare: database not open error insert into TableExample "No query Unable to fetch row" QSqlDatabasePrivate::database: unable to open database: "out of memory Error opening database"

В методах connectToDataBase и openToDataBase пропишите свой путь к базе данных.

z

Тоесть надо создать пустую базу sqlite сторонним ПО?

Нет. нужно указать корректный пути к базе данных. Если вы скопипастили код, то скорее всего у вас нет директории example на диске C, поэтому и не фурычит.

v

Здравствуйте, очень понравился Ваш урок. Сначала решил взять идею - не получилось, причем ошибки странные. После этого решил взять Ваш код и выяснилось, что ошибки остаются теми же. Компилятор ругается на то, что вроде как конструктор и деструктор класса DataBase должны быть виртуальными...Плохое решение проблемы, но все же отчасти ее решение - это убрать макрос Q_Object в заголовочном файле DataBase. При таком решении проблемы выскакивает ошибка " ASSERT: "!isEmpty()" in file ....." и программа сворачивается. Пожалуйста, подскажите в чем загвоздка, почему так и как это все исправить? Есть предположение, что это связано с настройкой компилятора, но...не знаю.

v

К посту выше...Проблема "виртуальных" конструктора и деструктора решается с помощью перезапуска qmake (его вообще нужно всегда перезапускать, если добавляешь макрос Q_Object в свой класс). Но вторая проблема, к сожалению осталась актуальной((  "ASSERT: "!isEmpty()" in file C:/Users/qt/work/qt/qtbase/src/corelib/tools/qlist.h, line 345 "

Добрый день! В методе connectToDataBase вы прописали свой путь к базе данных?

v

Да

Сейчас сразу и не припомню, было ли нечто такое, может в новых версиях Qt что-то поменяли, что генерирует ошибку. После работы гляну, какая там может быть проблема.

v

Хорошо, спасибо большое! Очень жду Вашего ответа. Я тоже пока попробую разобраться и может реализовать как-нибудь по-другому.

v

У меня все заработало. Не могу точно сказать в чем была причина, есть только догадки. Но программа заработала только после того, как я удалил базу данных с таким же названием, которая была создана мною вручную. Наверное, это мешало корректной работе программы, но не совсем ясно почему...ведь мы всего лишь обращаемся к этой БД с запросами и все.

Скорее всего дело в метаинформации, которой не было при создании файла вручную. При подключении некорректный файл крашил программу...

v

Да, спасибо Вам большое! У Вас очень интересные уроки!!!

Спасибо за отзыв. Будут вопросы не по статьям, то не стесняйтесь задавать их на форуме сайта .

B

DataBase is unknown type. Подскажите, в чем проблема, вроде все заголовочные подключаю

B

Все понял

ММм.. хорошо, А что там не так было?

B

Я не подключил header класса, поэтому он не знал тип такой. Подскажите пожалуйста

Я qDebug()`ом вызываю значения, которые подаю в базу данных, как я понимаю, база ругается на QDate и QTime


QDate("2018-10-08")

QTime("13:37:04.454")

26500

"Получено сообщение от 26500"

error insert into TableExample

" Parameter count mismatch"

Наверное у вас базад данных не создалась, поправьте путь к место, где должна создаться база данных вот в этом методе

bool DataBase::openDataBase()
{
    /* База данных открывается по заданному пути
     * и имени базы данных, если она существует
     * */
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setHostName(DATABASE_HOSTNAME);
    db.setDatabaseName("C:/example/" DATABASE_NAME);
    if(db.open()){
        return true;
    } else {
        return false;
    }
}


B
bool DataBase::openDataBase()
{
    db = QSqlDatabase::addDatabase("QSQLITE");

    db.setHostName(DATABASE_HOSTNAME);
    db.setDatabaseName("C:/Users/esmit/Documents/Qt/DataBase/" DATABASE_NAME);

    if(db.open())
        return true;
    else
        return false;
}


B

В отладчике возвращает true, проблема не в этом

Возможно, что пока вы повторяли работу кода из данной статьи, была создана таблица, но с другим количествов столбцов, в результату строка не добавляется из-за различий в запросе и самой таблице. Попробуйте удались базу данных, сконтролируйте, чтобы столбцы соответсвовали тому, что вы пытаетесь добавить в таблицу.

Спасибо за урок! Вопрос: что нужно сделать, чтобы в tableView показывалось, например, 12:56, а не 12:56:34.554?

Наследоваться от QSqlTableModel и переопределить метод data для Qt::DisplayRole. Там сделать необходимые проебразования формата.

И потом уже использовать переопределённую модель вместо QSqlTableModel

Сделал вот так. В tableView ничего нет, кроме заголовка.

QVariant MySqlTableModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole)
    {
        QTime time = this->data(index, Qt::DisplayRole).toTime();
        QString str = time.toString("hh:mm:ss");
        return str;
    }
    else
        return QVariant();
}

потому, что нужно сохранять информацию для всех остальных ролей и столбцов через вызов переопределённого метода. Да к тому же вы ещё и зациклили вызов метода data.

QVariant MySqlTableModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole)
    {
        QTime time = QSqlTableModel::data(index, Qt::DisplayRole).toTime();
        QString str = time.toString("hh:mm:ss");
        return str;
    }

    return QSqlTableModel::data(index, role);
}

Всё равно пусто, хотя строка с данными в базу добавляется.

Заработало. Забыл model->select(); вписать.

Спасибо!

Ещё небольшой вопрос. Я добился, чтобы в tableView в колонке flag показывался чекбокс (через флаг Qt::ItemIsUserCheckable) и управлял содержимым базы . Как теперь убрать цифру из поля, чтобы остался только чекбокс? flag.JPG flag.JPG

Переопределить метод data для той колонки и роли Qt::DisplayRole, чтобы в том случае возвращался QVariant() я так думаю...
Но возможно, что у вас там будут нюансы, если вы туда чекбокс запихали

Получилось приемлемо. Спасибо! Нюанс только в том, что поле рядом с чекбоксом не пропадает, оно просто пустое, что видно при выделении ячейки. Но этого достаточно.

Если будет не приемлемо потом, то тогда через кастомный Item Delegate нужно будет перерисовать ячейки в той колонке.

Не проще тогда использовать сразу кастомный делегат с чекбоксом? Я попробовал, но там засада в том, что чекбокс показывается только при щелчке на ячейку, а дефолтно показывается значение. Как эту засаду обойти, я не смог придумать, плохо ещё ориентируюсь в моделях.

Создайте тогда тему здесь на форуме в разделе Qt с выкладками кода и вашими попытками внедрения делегата, позже гляну или может кто-то ещё глянет из опытных пользователей.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
Looking for a Job?
14,000.00 руб. - 40,000.00 руб.
Разработчик Qt
Annino, Moscow Oblast, Russia
5,000.00 руб. - 15,000.00 руб.
Дизайнер
Moskovskiy, Moscow, Russia
25,000.00 руб. - 30,000.00 руб.
Разработчик Qt/C++
Barnaul, Altai Krai, Russia

For registered users on the site there is a minimum amount of advertising

A
Aug. 22, 2019, 11:24 p.m.
Aleksandr73

Qt - Test 001. Signals and slots

  • Result:47points,
  • Rating points-6
Aug. 21, 2019, 10:23 a.m.
Andrej Ermoshin

C++ - Test 002. Constants

  • Result:58points,
  • Rating points-2
Aug. 21, 2019, 10:15 a.m.
Andrej Ermoshin

C++ - Test 001. The first program and data types

  • Result:86points,
  • Rating points6
Last comments
Aug. 19, 2019, 7:41 a.m.
Andrej Jankovich

это проблема дистрибутива, попробуйте установить через пакетный менеджер snap Суть проблемы: libQt5Core которая лежит в дистрибутиве требует версию glibc >= 2.25 у вас видимо …
b
Aug. 18, 2019, 6:09 a.m.
bbb116

cqtdeployer /home/aleks/CQtDeployer/bin/cqtdeployer: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.25' not found (required by /home/aleks/CQtDeployer/lib/libQt5Core.so.5) linux mint …
D
Aug. 17, 2019, 9:04 a.m.
Damir

github ChekableTView Правой групповая смена значения при перетаскивании левой как обычно.
Aug. 16, 2019, 1:03 p.m.
Evgenij Legotskoj

Потому, что в минуте 60 секунд
Aug. 16, 2019, 12:16 p.m.
Dmitrij

а почему делитель 60000, а не 1000?
Now discuss on the forum
Aug. 24, 2019, 7:21 a.m.
Evgenij Legotskoj

Не помню, давно уже с QML не работал, по-моему, обычно пишет в консоль, что не находит файл. В любом случае какую-то ошибку в консоль выкидывает. Но если честно, если у вас проект будет ак…
BG
Aug. 24, 2019, 4:27 a.m.
Brjus Gliff

Спасибо, вначале в документации было не понятно что к чему, теперь разобрался
I
Aug. 21, 2019, 8:36 a.m.
Intruder

Александр, мне не нужно перебирать. Вы говорите правильно, сначала я написал избыточный код просто не подумав. Задача такая, мне нужно просто переложить из QMap в атрибуты xml тега все, что там …
Aug. 21, 2019, 3:16 a.m.
nayk1982

Если Вы разрабатываете какую-то универсальную утилиту, которая вообще не привязана к логике, тогда как вариант: 1. Получить список таблиц через QSqlDatabase::tables 2. Для каждой табли…
EVILEG
About
Services
© EVILEG 2015-2019
Recommend hosting TIMEWEB