Um die in einer Datenbanktabelle enthaltenen Informationen darzustellen, verwendet das Qt-Framework mehrere Klassen:
- QSqlQueryModel - ein Modell, das eine Tabelle durch Angabe einer rohen SQL-Abfrage bildet. Es kann nützlich sein, wenn Sie besonders anspruchsvolle Filter erstellen und Informationen aus verschiedenen Datenbanktabellen zusammenstellen. Mehr dazu in den folgenden Lektionen.
- QSqlTableModel - das Thema unseres Gesprächs in diesem Artikel. Ein Modell, das eine Tabelle mit dem Namen der Tabelle bildet, die in der Datenbank vorhanden ist. Von den Minuspunkten können wir das Fehlen von Methoden zum Verbinden von Links mit anderen Tabellen feststellen, um Werte in Feldern aus anderen Tabellen nach ID zu ersetzen.
- QSqlRelationalTableModel - eine Klasse, die es Ihnen ermöglicht, eine Tabelle mit Links aus anderen Tabellen zu bilden und die Werte der Tabelle, die dieses Modell darstellt, gemäß den IDs der in anderen Tabellen enthaltenen Datensätze zu ersetzen.
Für komfortables Arbeiten mit Informationen, die in die Datenbank gestellt werden, wird eine zusätzliche Klasse verwendet, die teilweise ein Gestaltungsmuster "Fassade" ist.
Der Programmcode wurde in QtCreator 3.3.1 basierend auf Qt 5.4.1 geschrieben.
Projektstruktur für QSqlTableModel
Das Projekt wird als Qt-Widgets-Anwendung erstellt, in der die folgenden Dateien erstellt werden:
- DataBase.pro - Profil;
- mainwindow.h - Header-Datei des Hauptanwendungsfensters;
- mainwindow.cpp - Fensterquellcode;
- main.cpp - die Hauptquelldatei, von der aus die Anwendung startet;
- mainwindow.ui - Form des Hauptanwendungsfensters;
- database.h - Header-Datei der Hilfsklasse, die verwendet wird, um mit Informationen zu arbeiten, die in der Datenbank platziert sind;
- database.cpp - die Quelldatei der Hilfsklasse, die verwendet wird, um mit Informationen zu arbeiten, die in der Datenbank platziert sind;
Notiz. Ich erstelle den größten Teil der Benutzeroberfläche im Designer, um die Logik des Hauptcodes nicht mit unnötigen Informationen zu überladen. Tatsächlich ist dies nur eine Frage des Geschmacks und der Gewohnheit.
mainwindow.ui
Formular für QSqlTableModel Erstellen Sie ein Formular für die Testanwendung, das ein QTableView Objekt namens tableView von der GUI verwendet.
DataBase.pro
Dem Projektprofil muss eine Direktive hinzugefügt werden, die die Verwendung von SQL-Bibliotheken deklariert.
#------------------------------------------------- # # 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
Die Datei wird im Projekt verwendet und wird standardmäßig erstellt.
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
mainwindow.h
Diese Header-Datei enthält die Deklaration des QSqlTableModel und DataBase Objekts, einer Hilfsklasse, die verwendet wird, um mit Informationen zu arbeiten, die in der Datenbank platziert werden. Darüber hinaus werden Methoden zur Initialisierung des Erscheinungsbilds und des Modells QSqlTableModel unserer Anwendung deklariert.
#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; /* В проекте используются объекты для взаимодействия с информацией в базе данных * и моделью представления таблицы базы данных * */ DataBase *db; QSqlTableModel *model; private: /* Также присутствуют два метода, которые формируют модель * и внешний вид TableView * */ void setupModel(const QString &tableName, const QStringList &headers); void createUI(); }; #endif // MAINWINDOW_H
mainwindow.cpp
Die Quelldatei, in der alle Hauptaktionen für die Interaktion mit dem Datenpräsentationsmodell stattfinden.
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); /* Первым делом необходимо создать объект, который будет использоваться для работы с данными нашей БД * и инициализировать подключение к базе данных * */ db = new DataBase(); db->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)); // Вставляем данные в БД db->inserIntoTable(data); } /* Инициализируем модель для представления данных * с заданием названий колонок * */ this->setupModel(TABLE, QStringList() << trUtf8("id") << trUtf8("Дата") << trUtf8("Время") << trUtf8("Рандомное число") << trUtf8("Сообщение") ); /* Инициализируем внешний вид таблицы с данными * */ this->createUI(); } MainWindow::~MainWindow() { delete ui; } /* Метод для инициализации модеи представления данных * */ void MainWindow::setupModel(const QString &tableName, const QStringList &headers) { /* Производим инициализацию модели представления данных * с установкой имени таблицы в базе данных, по которому * будет производится обращение в таблице * */ model = new QSqlTableModel(this); model->setTable(tableName); /* Устанавливаем названия колонок в таблице с сортировкой данных * */ for(int i = 0, j = 0; i < model->columnCount(); i++, j++){ model->setHeaderData(i,Qt::Horizontal,headers[j]); } // Устанавливаем сортировку по возрастанию данных по нулевой колонке model->setSort(0,Qt::AscendingOrder); } void MainWindow::createUI() { ui->tableView->setModel(model); // Устанавливаем модель на TableView ui->tableView->setColumnHidden(0, true); // Скрываем колонку с id записей // Разрешаем выделение строк ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); // Устанавливаем режим выделения лишь одно строки в таблице ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection); // Устанавливаем размер колонок по содержимому ui->tableView->resizeColumnsToContents(); ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->tableView->horizontalHeader()->setStretchLastSection(true); model->select(); // Делаем выборку данных из таблицы }
database.h
Um die Frage der Interaktion mit der Datenbank nicht im Dunkeln zu lassen, versorge ich Listings dieser Klasse auch mit Kommentaren. Die Arbeit wird mit der SQLite-Datenbank erledigt, aber das Prinzip der Arbeit mit Netzwerkdatenbanken ist dasselbe, mit Ausnahme der Netzwerkverbindung und der Nuancen der Arbeit mit jeder spezifischen Datenbank.
In der Header-Datei der DataBase -Klasse ist es für die weitere Arbeit erforderlich, Direktiven für die Namen der Tabelle und der Tabellenspalten anzugeben. Und auch die Methoden, die verwendet werden, um mit der Datenbank zu interagieren, sowie Schnittstellen für diese Interaktion mit übergeordneten Schichten der Anwendung bereitzustellen.
#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
datenbank.cpp
Die Quelldatei für eine Hilfsklasse, die verwendet wird, um mit Informationen zu arbeiten, die in der Datenbank platziert werden. In diesem Fall wird die Bildung von SQL-Abfragen in dieser Klasse maximal ausgeblendet, um die Anzahl der Verknüpfungen im Programmcode zu reduzieren. Eine solche Praxis am Anfang kann einem Anfänger helfen, sofort Code zu schreiben, in dem verschiedene Klassen so weit wie möglich voneinander isoliert sind, was einer der Aspekte von OOP ist.
#include "database.h" DataBase::DataBase(QObject *parent) : QObject(parent) { } 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; }
Ergebnis
Als Ergebnis sollten Sie eine Platte der folgenden Form mit den Daten erhalten, die vorbereitet und in den Konstruktor des Hauptanwendungsfensters eingefügt wurden.
Anwendungsdarstellung mit QSqlTableModel
Здравствуйте, а, скажите, сам объект 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())