Для представления таблиц баз данных в 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 с данными выдернутыми из базы данных, как показано на рисунке. Также рекомендую видеоурок по данной статье.
Добрый день.
Можно ли как то вставить картинку в TableView? Допустим в бд есть boolean столбец который говорит нам добавлен ли объект в избранное, как можно исходя из данных этого поля отобразить ту или иную иконку в TableView?
Пробовал так хотя бы отобразить иконку в 1 колонке добавив в data()
Но иконка вставляется текстом
Ну что, ты разобрался?