Evgenii Legotckoi
Evgenii Legotckoi5 ноября 2015 г. 3:26

QML - Урок 011. Передача данных из QSqlQueryModel в Qml 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 с данными выдернутыми из базы данных, как показано на рисунке. Также рекомендую видеоурок по данной статье.

Видеоурок

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

AK
  • 1 ноября 2020 г. 4:56
  • (ред.)

Добрый день.
Можно ли как то вставить картинку в TableView? Допустим в бд есть boolean столбец который говорит нам добавлен ли объект в избранное, как можно исходя из данных этого поля отобразить ту или иную иконку в TableView?
Пробовал так хотя бы отобразить иконку в 1 колонке добавив в data()

    if ( role==FavRole && index.column() == 0) {
       return QIcon("D:/Users/Downloads/ico.ico");
     }

Но иконка вставляется текстом

R
  • 17 июня 2023 г. 4:15

Ну что, ты разобрался?

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50баллов,
  • Очки рейтинга-4
m
  • molni99
  • 26 октября 2024 г. 11:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80баллов,
  • Очки рейтинга4
m
  • molni99
  • 26 октября 2024 г. 11:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20баллов,
  • Очки рейтинга-10
Последние комментарии
ИМ
Игорь Максимов22 ноября 2024 г. 22:51
Django - Урок 017. Кастомизированная страница авторизации на Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi1 ноября 2024 г. 0:37
Django - Урок 064. Как написать расширение для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 октября 2024 г. 18:19
Читалка fb3-файлов на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5 октября 2024 г. 17:51
Django - Урок 064. Как написать расширение для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55 июля 2024 г. 21:02
QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Сейчас обсуждают на форуме
Evgenii Legotckoi
Evgenii Legotckoi25 июня 2024 г. 1:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 ноября 2024 г. 17:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject4 июня 2022 г. 13:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 октября 2024 г. 19:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Следите за нами в социальных сетях