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.
Здравствуйте, а, скажите, сам объект QSqlTableModel предоставляет доступ к данным, которые уже есть в таблице? Например, получить в какую - то переменную ранее записанное рандомное число из строки с индексом n?Или, это только возможно запросом
Метод setData(строка, столбец, значение) есть, а, метода, например, типа, getData(строка, столбец), не нашел
Разобрался QSqlRecord rec = model(номер строки) - получаем строку int num = rec.value("TABLE_RANDOM"); - получаем поле
Всё гораздо проще, метод, который Вы искали называется не getData() , а просто data() . Для получения данных нужно дернуть QModelIndex из модели. Выглядеть это будет так:
Метод вернёт QVariant , который будет содержать данные.
А у меня выхлоп: Не удалось восстановить базу данных 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 пропишите свой путь к базе данных.
Тоесть надо создать пустую базу sqlite сторонним ПО?
Нет. нужно указать корректный пути к базе данных. Если вы скопипастили код, то скорее всего у вас нет директории example на диске C, поэтому и не фурычит.
Здравствуйте, очень понравился Ваш урок. Сначала решил взять идею - не получилось, причем ошибки странные. После этого решил взять Ваш код и выяснилось, что ошибки остаются теми же. Компилятор ругается на то, что вроде как конструктор и деструктор класса DataBase должны быть виртуальными...Плохое решение проблемы, но все же отчасти ее решение - это убрать макрос Q_Object в заголовочном файле DataBase. При таком решении проблемы выскакивает ошибка " ASSERT: "!isEmpty()" in file ....." и программа сворачивается. Пожалуйста, подскажите в чем загвоздка, почему так и как это все исправить? Есть предположение, что это связано с настройкой компилятора, но...не знаю.
К посту выше...Проблема "виртуальных" конструктора и деструктора решается с помощью перезапуска qmake (его вообще нужно всегда перезапускать, если добавляешь макрос Q_Object в свой класс). Но вторая проблема, к сожалению осталась актуальной(( "ASSERT: "!isEmpty()" in file C:/Users/qt/work/qt/qtbase/src/corelib/tools/qlist.h, line 345 "
Добрый день! В методе connectToDataBase вы прописали свой путь к базе данных?
Да
Сейчас сразу и не припомню, было ли нечто такое, может в новых версиях Qt что-то поменяли, что генерирует ошибку. После работы гляну, какая там может быть проблема.
Хорошо, спасибо большое! Очень жду Вашего ответа. Я тоже пока попробую разобраться и может реализовать как-нибудь по-другому.
У меня все заработало. Не могу точно сказать в чем была причина, есть только догадки. Но программа заработала только после того, как я удалил базу данных с таким же названием, которая была создана мною вручную. Наверное, это мешало корректной работе программы, но не совсем ясно почему...ведь мы всего лишь обращаемся к этой БД с запросами и все.
Скорее всего дело в метаинформации, которой не было при создании файла вручную. При подключении некорректный файл крашил программу...
Да, спасибо Вам большое! У Вас очень интересные уроки!!!
Спасибо за отзыв. Будут вопросы не по статьям, то не стесняйтесь задавать их на форуме сайта .
DataBase is unknown type. Подскажите, в чем проблема, вроде все заголовочные подключаю
Все понял
ММм.. хорошо, А что там не так было?
Я не подключил header класса, поэтому он не знал тип такой. Подскажите пожалуйста
Я qDebug()`ом вызываю значения, которые подаю в базу данных, как я понимаю, база ругается на QDate и QTime
QDate("2018-10-08")
QTime("13:37:04.454")
26500
"Получено сообщение от 26500"
error insert into TableExample
" Parameter count mismatch"
Наверное у вас базад данных не создалась, поправьте путь к место, где должна создаться база данных вот в этом методе
В отладчике возвращает true, проблема не в этом
Возможно, что пока вы повторяли работу кода из данной статьи, была создана таблица, но с другим количествов столбцов, в результату строка не добавляется из-за различий в запросе и самой таблице. Попробуйте удались базу данных, сконтролируйте, чтобы столбцы соответсвовали тому, что вы пытаетесь добавить в таблицу.
Спасибо за урок!
Вопрос: что нужно сделать, чтобы в tableView показывалось, например, 12:56, а не 12:56:34.554?
Наследоваться от QSqlTableModel и переопределить метод data для Qt::DisplayRole. Там сделать необходимые проебразования формата.
И потом уже использовать переопределённую модель вместо QSqlTableModel
Сделал вот так. В tableView ничего нет, кроме заголовка.
потому, что нужно сохранять информацию для всех остальных ролей и столбцов через вызов переопределённого метода. Да к тому же вы ещё и зациклили вызов метода data.
Всё равно пусто, хотя строка с данными в базу добавляется.
Заработало. Забыл model->select(); вписать.
Спасибо!
Ещё небольшой вопрос.
Я добился, чтобы в tableView в колонке flag показывался чекбокс (через флаг Qt::ItemIsUserCheckable) и управлял содержимым базы . Как теперь убрать цифру из поля, чтобы остался только чекбокс?
flag.JPG
Переопределить метод data для той колонки и роли Qt::DisplayRole, чтобы в том случае возвращался QVariant() я так думаю...
Но возможно, что у вас там будут нюансы, если вы туда чекбокс запихали
Получилось приемлемо. Спасибо!
Нюанс только в том, что поле рядом с чекбоксом не пропадает, оно просто пустое, что видно при выделении ячейки. Но этого достаточно.
Если будет не приемлемо потом, то тогда через кастомный Item Delegate нужно будет перерисовать ячейки в той колонке.
Не проще тогда использовать сразу кастомный делегат с чекбоксом? Я попробовал, но там засада в том, что чекбокс показывается только при щелчке на ячейку, а дефолтно показывается значение. Как эту засаду обойти, я не смог придумать, плохо ещё ориентируюсь в моделях.
Создайте тогда тему здесь на форуме в разделе Qt с выкладками кода и вашими попытками внедрения делегата, позже гляну или может кто-то ещё глянет из опытных пользователей.
Всем привет. Возник вопрос, можно ли работать с QtSql без сырых sql запросов, как в различный orm, через классы qsqltablemodel, qsqlrelationtablemodel?
Добрый день. В Qt нет ORM в QtSql, а классы qsqltablemodel и qsqlrelationtablemodel дают относительно слабый функционал, даже для использования фильтрации потребуется писать кусок SQL запроса. Без сырых запросов не обойдётесь.
Спасибо. Очень жаль, хотя с другой стороны это дает гораздо больше гибкости.
Вы заблуждаетесь. Любая нормальная ORM позволяет выполнение сырых SQL запросов. А если хорошо разобраться в работе моделей данных в Qt, то не составит труда использовать ORM вместе с Qt, ту же самую ORM Wt::Dbo. Нет особой гибкости в том, что приходится тратить уйму времени на написание сырых запросов, когда ORM позволяет всё это выполнять значительно быстрее. Я думаю, что со стороны Qt Company - это является недоработкой, что они не попытались сделать свою ORM, ресурсы им сейчас это позволяют. Так что действительно жаль, что этого нет из коробки.
Спасибо за инфу. Поиск качественной ORM привел меня только к sqlite_orm, но не подходит из-за необходимости полноценной поддержки c++14. Про framework Wt не слышал, спасибо за наводку.
Рекомендую Wt, достаточно мощная вещь. Этот фреймворк может использоваться для написания сайтов на C++, либо можно использовать только отдельный компоненты, например только ORM. Но я не знаю, какая требуется минимальная версия стандарта C++ для него. А также он требует boost библиотеку. У нас в проекте используется boost, а также C++17. Поэтому никогда не задумывался о минимальных требованиях к этому фреймворку.
не удается подключиить библеотеку
include "database.h"
выдает ошибку. Можете помочь?
Потому что это файл который нужно создать, а не библиотека.
В статье есть содержание этого файла. Добавляйте в проект. Копируйте содержимое из статьи.
полностью повторил структору проекта. В форму дабавил tableView. Но при запуске получаю форму только с пустым tableView. Можете подсказать в чем пробелма?
У вас база данных не открылась
Исправьте путь к базе данных на свой корректный в следующих методах
спасибо, все сработало
Подскажите пожалуйста как изменить данные в базе, тип столбцов и сделать столбцы шире
Напишите метод с SQL запросом, который изменит данные или их тип. Это ALTER методы для изменения типа данных, а также INSERT и UPDATE методы для добавления и обновления данных. Это вы можете найти в документации на SQL. Просто напишите QSqlQuery запросы, как это показано в методе inserIntoTable
Что касается ширины столбцов, то QTableView имеет метод setColumnWidth
Почему-то такой метод для обновления не работает, который можно было бы применить в данном примере. То есть в представлении данные удаляются и обновляются, а в базе данных изменений не происходит:
А также для удаления такой метод не обновляет базу данных SQLite (модель унаследована от QSqlTableModel):
Не могу понять как связывается БД и модель. Подскажите, пожалуйста.
model = new QSqlTableModel(this);
model->setTable(tableName);
Это строки 61-62 в mainwindow.cpp
IscanderChe. Мы создаем объект класс DataBase. Как этот объект связать с объектом класса QSqlTableMode?
QSqlTableModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase())
Мой вопрос был про объект класса DataBase. Как он связывается с объектом класса QSqlTableModel.Можете указать строчку в коде?
Объект QSqlDatabase() устанавливается глобально, для всего кода. DataBase, соответственно, делает то же самое, только с помощью своих методов, которые используются в mainwindow.cpp, а сам объект подключается и объявляется в mainwindow.h. Затем через таблицы из БД происходит связь с моделью. Это про первый мой ответ.
Если вы, к примеру, открываете несколько баз данных в одном приложении, то вы можете указать при создании модели, какую именно базу данных использовать. Это про мой второй ответ вам.
error insert into TableExample
" Количество параметров не совпадает"
Я путь свой прописывала и даже бд удаляла, чтобы заново сделать, не работает. (всё остальное как у вас... Вроде как. Хотя я так и не увидела, где используется closeDataBase())