Evgenii Legotckoi
Evgenii Legotckoi05 листопада 2015 р. 03:26

QML - Урок 011. Передача даних з QML 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

база даних.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
  • 01 листопада 2020 р. 04:56
  • (відредаговано)

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

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

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

R
  • 17 червня 2023 р. 04:15

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
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 Legotckoi01 листопада 2024 р. 00:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 18:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 17:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 21:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi25 червня 2024 р. 01:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 17:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 13:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 19:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах