Мәліметтер қоры кестесіндегі ақпаратты көрсету үшін Qt құрылымы бірнеше класстарды пайдаланады:
- QSqlQueryModel – өңделмеген SQL сұранысын көрсету арқылы кестені жасайтын модель. Бұл әсіресе күрделі сүзгілерді жасау және әртүрлі дерекқор кестелерінен ақпаратты құрастыру кезінде пайдалы болуы мүмкін. Бұл туралы толығырақ келесі сабақтарда.
- QSqlTableModel - - бұл мақаладағы талқылау тақырыбы. Дерекқорда бар кестенің аты бойынша кестені жасайтын модель. Кемшіліктердің бірі идентификатор бойынша басқа кестелердегі өрістерге мәндерді ауыстыру үшін басқа кестелермен қарым-қатынастарды қосу әдістерінің жоқтығын атап өтуге болады.
- QSqlRelationalTableModel - басқа кестелердегі жазбалардың идентификаторына сәйкес осы үлгі көрсететін кестенің мәндерін ауыстыра отырып, басқа кестелердің қатынастары бар кестені құруға мүмкіндік беретін класс.
Деректер базасында орналастырылған ақпаратпен ыңғайлы жұмыс істеу үшін қосымша сынып пайдаланылады, ол ішінара дизайн үлгісі болып табылады «Қасбет» .
Код Qt 5.4.1 негізінде QtCreator 3.3.1-де жазылған.
QSqlTableModel үшін жоба құрылымы
Жоба келесі файлдарды жасайтын Qt Widgets қолданбасы ретінде жасалған:
- DataBase.pro - профиль;
- mainwindow.h – қосымшаның негізгі терезесінің тақырып файлы;
- mainwindow.cpp – терезенің бастапқы коды;
- main.cpp – қолданба басталатын негізгі бастапқы файл;
- mainwindow.ui – қосымшаның негізгі терезесінің формасы;
- database.h – дерекқорда орналастырылған ақпаратпен жұмыс істеу үшін қолданылатын көмекші кластың тақырып файлы;
- database.cpp – дерекқорда орналастырылған ақпаратпен жұмыс істеу үшін қолданылатын көмекші класстың бастапқы файлы;
Ескерту. Мен негізгі кодтың логикасын қажетсіз ақпаратпен шатастырмау үшін дизайнерде интерфейстің көп бөлігін жасаймын. Шындығында, бұл тек дәм мен әдетке байланысты мәселе .
mainwindow.ui
QSqlTableModel пішіні Біз графикалық интерфейстен tableView деп аталатын QTableView нысанын қолданатын сынақ қолданбасы үшін пішін жасаймыз.
DataBase.pro
Жоба профилінде 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
Файл жобада пайдаланылады, әдепкі бойынша жасалады.
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
mainwindow.h
Бұл тақырып файлында QSqlTableModel және DataBase нысанының мәлімдемесі бар, дерекқорда орналастырылған ақпаратпен жұмыс істеу үшін қолданылатын көмекші сынып. Оларға қосымша біздің қолданбаның QSqlTableModel көрінісі мен үлгісін инициализациялау әдістері жарияланған.
#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
Деректерді көрсету үлгісімен барлық негізгі өзара әрекеттесу орын алатын бастапқы файл.
#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(); // Делаем выборку данных из таблицы }
дерекқор.h
Дерекқормен өзара әрекеттесу мәселесін толық қараңғылықта қалдырмау үшін мен осы сыныптың тізімдерін түсініктемелермен ұсынамын. Жұмыс SQLite дерекқорымен орындалады, бірақ желілік қосылымды және әрбір нақты дерекқормен жұмыс істеудің нюанстарын қоспағанда, желілік деректер базасымен жұмыс істеу принципі ұқсас болады.
DataBase класының тақырып файлында әрі қарай жұмыс істеуге ыңғайлы болу үшін кесте атаулары мен кесте бағандарына арналған директиваларды көрсету керек. Сондай-ақ дерекқормен өзара әрекеттесу үшін қолданылатын әдістер, сонымен қатар қолданбаның жоғары деңгейлі қабаттарымен өзара әрекеттесу үшін интерфейстерді қамтамасыз ету.
#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
Дерекқорда орналастырылған ақпаратпен жұмыс істеу үшін пайдаланылатын көмекші сыныптың бастапқы файлы. Бұл жағдайда бағдарлама кодындағы сілтемелер санын азайту үшін бұл класста SQL сұраныстарының қалыптасуы максималды түрде жасырылады. Бастапқыда мұндай тәжірибе бастаушыға әртүрлі сыныптар мүмкіндігінше бір-бірінен бөлінген кодты дереу жазу мүмкіндігін қалыптастыруға көмектеседі, бұл OOP аспектілерінің бірі болып табылады.
#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; }
Барлығы
Нәтиже негізгі қолданба терезесінің конструкторында дайындалған және енгізілген деректермен келесі пішіндегі кесте болуы керек.
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())