Политика конфиденциальностиКонтактыО сайтеОтзывыGitHubDonate
© EVILEG 2015-2018
Рекомендует хостинг
TIMEWEB

QML - Урок 011. Передача данных из QSqlQueryModel в Qml TableView

QML, SQL, Qt, DataBase, QSqlQueryModel, TableView

Для представления таблиц баз данных в TableView при разработке с использованием QML можно использовать класс, наследованный от QSqlQueryModel. Для этого необходимо в наследованном классе определить метод, который установит соответствие ролей колонок таблицы к соответствующим колонкам в TableView, определенном в QML, где также указаны роли для каждого объекта TableViewColumn, то есть для каждой колонки. Также необходимо будет переопределить метод QVariant data( ... ) const , который возвращает данные для ячеек таблицы. В данном случае информация будет возвращаться в соответствии с определёнными ролями колонок таблицы.

Структура проекта для работы с TableView

Проект состоит из следующих файлов:

  • QmlSqlQueryModel.pro - профайл проекта;
  • database.h - заголовочный для создания и инициализации тестовой базы данных;
  • database.cpp - файл исходных кодов для создания и инициализации тестовой базы данных;
  • model.h - заголовочный файл модели данных;
  • model.cpp - файл исходных кодов модели данных;
  • main.cpp - основной исходный файл проекта;
  • main.qml - qml файл с TableView.

QmlSqlQueryModel.pro

Обязательно подключите модуль SQL в проект в данном файле. Иначе библиотека QSqlQueryModel не будет найдена при компиляции проекта.

TEMPLATE = app

QT += qml quick widgets sql

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

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Default rules for deployment.
include(deployment.pri)

HEADERS += \
    database.h \
    model.h

database.h

Заголовочный файл класса-обёртки для инициализации подключения к базе данных и её создания в случае, если база данных отсутствует. Данный вспомогательный класс фигурировал в ряде более ранних уроков. Например, при работе QSqlTableModel или QSqlRelationalTableModel . Поэтому не буду заострять внимание на нём, а лишь приведу код. И отмечу, что с помощью этого класса создаётся или открывается (если уже создана) база данных в которую при каждом открытии помещается четыре строки. А каждая строка состоит из четырёх колонок: даты ("date"), времени ("time"), псевдослучайного числа ("random") и сообщения о данном числе ("message").

ВНИМАНИЕ!!! - файл базы данных создается в папке C:/example , поэтому или поправьте метод DataBase::connectToDataBase() или создайте папку example на диске C .

#ifndef DATABASE_H
#define DATABASE_H

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

/* Директивы имен таблицы, полей таблицы и базы данных */
#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();
    /* Методы для непосредственной работы с классом
     * Подключение к базе данных и вставка записей в таблицу
     * */
    void connectToDataBase();
    bool inserIntoTable(const QVariantList &data);

private:
    // Сам объект базы данных, с которым будет производиться работа
    QSqlDatabase    db;

private:
    /* Внутренние методы для работы с базой данных
     * */
    bool openDataBase();
    bool restoreDataBase();
    void closeDataBase();
    bool createTable();
};

#endif // DATABASE_H

database.cpp

#include "database.h"

DataBase::DataBase(QObject *parent) : QObject(parent)
{
    // Подключаемся к базе данных
    this->connectToDataBase();
    /* После чего производим наполнение таблицы базы данных
     * контентом, который будет отображаться в TableView
     * */
    for(int i = 0; i < 4; i++){
        QVariantList data;
        int random = qrand(); // Получаем случайные целые числа для вставки а базу данных
        data.append(QDate::currentDate()); // Получаем текущую дату для вставки в БД
        data.append(QTime::currentTime()); // Получаем текущее время для вставки в БД
        // Подготавливаем полученное случайное число для вставки в БД
        data.append(random);
        // Подготавливаем сообщение для вставки в базу данных
        data.append("Получено сообщение от " + QString::number(random));
        // Вставляем данные в БД
        inserIntoTable(data);
    }
}

DataBase::~DataBase()
{

}

/* Методы для подключения к базе данных
 * */
void DataBase::connectToDataBase()
{
    /* Перед подключением к базе данных производим проверку на её существование.
     * В зависимости от результата производим открытие базы данных или её восстановление
     * */
    if(!QFile("C:/example/" DATABASE_NAME).exists()){
        this->restoreDataBase();
    } else {
        this->openDataBase();
    }
}

/* Методы восстановления базы данных
 * */
bool DataBase::restoreDataBase()
{
    if(this->openDataBase()){
        if(!this->createTable()){
            return false;
        } else {
            return true;
        }
    } else {
        qDebug() << "Не удалось восстановить базу данных";
        return false;
    }
    return false;
}

/* Метод для открытия базы данных
 * */
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;
    }
}

/* Методы закрытия базы данных
 * */
void DataBase::closeDataBase()
{
    db.close();
}

/* Метод для создания таблицы в базе данных
 * */
bool DataBase::createTable()
{
    /* В данном случае используется формирование сырого SQL-запроса
     * с последующим его выполнением.
     * */
    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;
}

/* Метод для вставки записи в базу данных
 * */
bool DataBase::inserIntoTable(const QVariantList &data)
{
    /* Запрос SQL формируется из QVariantList,
     * в который передаются данные для вставки в таблицу.
     * */
    QSqlQuery query;
    /* В начале SQL запрос формируется с ключами,
     * которые потом связываются методом bindValue
     * для подстановки данных из 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());
    // После чего выполняется запросом методом exec()
    if(!query.exec()){
        qDebug() << "error insert into " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

model.h

А теперь самое интересное. Отнаследуемся от класса QSqlQueryModel и создадим собственный класс модели, который будет возвращать данные в соответствии с определёнными ролями колонок в TableView в Qml слое. То есть переопределяем метод для получения данных - это метод data() , а также метод roleNames() , который возвращает имена ролей в соответствии с которыми будут подставляться данные в TableView, отмечу, что имена должны будут совпадать.

#ifndef MODEL_H
#define MODEL_H

#include <QObject>
#include <QSqlQueryModel>

class Model : public QSqlQueryModel
{
    Q_OBJECT
public:
    // Перечисляем все роли, которые будут использоваться в TableView
    enum Roles {
        DateRole = Qt::UserRole + 1,    // дата
        TimeRole,                       // время
        RandomRole,                     // псевдослучаное число
        MessageRole                     // сообщение
    };

    // объявляем конструктор класса
    explicit Model(QObject *parent = 0);

    // Переопределяем метод, который будет возвращать данные
    QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;

protected:
    /* хешированная таблица ролей для колонок.
     * Метод используется в дебрях базового класса QAbstractItemModel,
     * от которого наследован класс QSqlQueryModel
     * */
    QHash<int, QByteArray> roleNames() const;

signals:

public slots:
};

#endif // MODEL_H

model.cpp

#include "model.h"

Model::Model(QObject *parent) :
    QSqlQueryModel(parent)
{
    // Конструктор будет пустой ;-)
}

// Метод для получения данных из модели
QVariant Model::data(const QModelIndex & index, int role) const {

    // Определяем номер колонки, адрес так сказать, по номеру роли
    int columnId = role - Qt::UserRole - 1;
    // Создаём индекс с помощью новоиспечённого ID колонки
    QModelIndex modelIndex = this->index(index.row(), columnId);

    /* И с помощью уже метода data() базового класса
     * вытаскиваем данные для таблицы из модели
     * */
    return QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
}

// Метод для получения имен ролей через хешированную таблицу.
QHash<int, QByteArray> Model::roleNames() const {
    /* То есть сохраняем в хеш-таблицу названия ролей
     * по их номеру
     * */
    QHash<int, QByteArray> roles;
    roles[DateRole] = "date";
    roles[TimeRole] = "time";
    roles[RandomRole] = "random";
    roles[MessageRole] = "message";
    return roles;
}

main.cpp

А теперь, пользуясь приемами регистрации обращения к C++ объекту в QML слое из урока по сигналам и слотам в QML регистрируем кастомную модель данных в QML слое в качестве свойства, к которому можно обращаться по имени "myModel" из QML слоя. Не забыв, конечно выполнить SQL-запрос для получения данных.

#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

#include <database.h>
#include <model.h>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QQmlApplicationEngine engine;

    // Инициализируем базу данных
    DataBase database;
    // Объявляем и инициализируем модель представления данных
    Model *model = new Model();
    /* Поскольку Мы отнаследовались от QSqlQueryModel, то
     * для выборки данных нам необходимо выполнить SQL-запрос,
     * в котором мы выберем все необходимы поля из нужной нам таблицы
     * */
    model->setQuery("SELECT " TABLE_DATE ", " TABLE_TIME ", " TABLE_RANDOM ", " TABLE_MESSAGE
                   " FROM " TABLE);

    /* А это уже знакомо из уроков по сигналам и слотам в QML
     * Помещаем полученную модель в контекст QML, чтобы была возможность
     * обращаться к модели по имени "myModel"
     * */
    engine.rootContext()->setContextProperty("myModel", model);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

main.qml

И самое простое из всего примера - это установка TableView с колонками, которых распределены роли, по которым будут подставлены данные, а также установить саму модель по зарегистрированному имени "myModel".

import QtQuick 2.5
import QtQuick.Controls 1.4

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    TableView {
        anchors.fill: parent

        TableViewColumn {
            role: "date"    // Эти роли совпадают с названиями ролей в C++ модели
            title: "Date"
        }

        TableViewColumn {
            role: "time"    // Эти роли совпадают с названиями ролей в C++ модели
            title: "Time"
        }

        TableViewColumn {
            role: "random"  // Эти роли совпадают с названиями ролей в C++ модели
            title: "Random"
        }

        TableViewColumn {
            role: "message" // Эти роли совпадают с названиями ролей в C++ модели
            title: "Message"
        }

        // Устанавливаем модель в TableView
        model: myModel
    }
}

Итог

В результате всего выше перечисленного колдунства Вы получите приложение, в окне которого будет TableView с данными выдернутыми из базы данных, как показано на рисунке. Также рекомендую видеоурок по данной статье.

Видеоурок

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
A
22 января 2019 г. 21:22
Allyonz

C++ - Тест 001. Первая программа и типы данных

  • Результат:40баллов,
  • Очки рейтинга-8
A
22 января 2019 г. 14:15
Alex

Qt - Тест 001. Сигналы и слоты

  • Результат:89баллов,
  • Очки рейтинга6
IO
20 января 2019 г. 18:39
Ivan Otreshko

C++ - Тест 003. Условия и циклы

  • Результат:0баллов,
  • Очки рейтинга-10
Последние комментарии
22 января 2019 г. 13:17
Евгений Легоцкой

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

Не проще тогда использовать сразу кастомный делегат с чекбоксом? Я попробовал, но там засада в том, что чекбокс показывается только при щелчке на ячейку, а дефолтно показывается значение. Как ...
22 января 2019 г. 12:15
Евгений Легоцкой

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

Получилось приемлемо. Спасибо!Нюанс только в том, что поле рядом с чекбоксом не пропадает, оно просто пустое, что видно при выделении ячейки. Но этого достаточно.
22 января 2019 г. 11:50
Евгений Легоцкой

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

Зарание спасибо.А еще на эту тему можно будет сделать статью. Это контент будет уникальным.
23 января 2019 г. 7:37
Евгений Легоцкой

Hello. Maybe the English will be better for you? I think russian is not native language for you, or did I mistake? Did I undesrtood rightly, that you mean this documentation ( ...
I
22 января 2019 г. 13:45
IscanderChe

Всем добрый день. Суть задачи: надо, чтобы в одной из колонок QTableView вместо хранимого в QSqlTableModel значения выводился чекбокс и при смене состояния чекбокса значения в базе тоже ...
M
22 января 2019 г. 13:15
Max-P85

Евгений, огромное спасибо, все работает!
Присоединяйтесь к нам в социальных сетях

Для зарегистрированных пользователей на сайте присутствует минимальное количество рекламы