- 1. Жоба құрылымы
- 2. QmlDataBase.pro
- 3. main.cpp
- 4. дерекқор.h
- 5. database.cpp
- 6. listmodel.h
- 7. listmodel.cpp
- 8. main.qml
- 9. Барлығы
- 10. Бейне оқулық
QML Qt ішіндегі деректер қорымен жұмыс істеудің шағын мысалы. Бұл сабақ QML Qt ішіндегі сигналдар мен ұяшықтарды пайдалану, QML деңгейінен C++ сыныптарына қатынасу туралы ақпаратты біріктіреді. QML, ішінде қолданба интерфейсін іске асыру, сонымен қатар дерекқор кестесі үшін model/view енгізу.
Деректер базасында төрт бағаннан тұратын адамдар тізімі бар кесте бар:
- id (INTEGER) - бірегей жазба нөмірі;
- Аты (VARCHAR (255)) - Аты;
- Тегі (ВАРЧАР (255)) - Тегі;
- Ник (ВАРЧАР (255)) - Ник.
Қолданба қолданба интерфейсі арқылы дерекқорға жазбаларды жоюды және қосуды жүзеге асыруы керек. Дерекқорға жазбаларды қосу үшін деректерді енгізуге арналған үш өріс және SQL дерекқорына деректерді қосуды инициализациялайтын түйме пайдаланылады. Кестеге жазбаларды қосу осыған арналған әдіс бойынша орауыш класы арқылы жүзеге асырылады.Сонымен қатар қосымшада QML деңгейіндегі TableView-де ақпаратты көрсету үшін деректерді ұсыну моделін жүзеге асыратын ListModel класы бар.
Жоба құрылымы
- QmlDataBase.pro - жоба профилі;
- database.h - деректер қорымен жұмыс істеуге арналған орауыш класының тақырып файлы;
- database.cpp - деректер қорымен жұмыс істеуге арналған орауыш класының бастапқы код файлы;
- listmodel.h - деректер моделінің тақырып файлы;
- listmodel.cpp - деректер үлгісінің бастапқы код файлы;
- main.cpp - қолданбаның негізгі бастапқы файлы;
- main.qml - негізгі qml файлы.
QmlDataBase.pro
Мәліметтер қорымен жұмыс істеу үшін sql модулін, сонымен қатар қолданбаның жергілікті көрінісі үшін виджеттер модулін қосу керек.
TEMPLATE = app QT += qml quick widgets sql SOURCES += main.cpp \ database.cpp \ listmodel.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 \ listmodel.h
main.cpp
Негізгі бастапқы код файлында біз дерекқормен жұмыс істеуге арналған орауыш класын және деректер үлгісі класын қосамыз. Бұл сыныптардың объектілері осы файлда жарияланып, инициализациялануы керек және QML деңгейінен осы нысандарға және олардың қасиеттеріне қол жеткізу конфигурациялануы керек.
Нысандарға қол жеткізу конфигурацияланғаннан кейін, объектінің келесі сипаттары мен функциялары QML деңгейінде қол жетімді болады, олар өз класында келесідей жарияланады:
- Сигналдар
- Слоттар
- Сондай-ақ Q_PROPERTY макросында пайда болатын функциялар
#include <QApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "database.h" #include "listmodel.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); QQmlApplicationEngine engine; // Подключаемся к базе данных DataBase database; database.connectToDataBase(); // Объявляем и инициализируем модель данных ListModel *model = new ListModel(); // Обеспечиваем доступ к модели и классу для работы с базой данных из QML engine.rootContext()->setContextProperty("myModel", model); engine.rootContext()->setContextProperty("database", &database); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
дерекқор.h
Бұл класс мәліметтер қорымен жұмыс істеу әдістерін жариялайды:
- мәліметтер қорына қосылу әдістері, оны қалпына келтіру;
- мәліметтер базасына жазбаларды қосу әдістері;
- деректер кестесіндегі жазбаларды жою әдістері.
Дерекқорға қосылу QSqlQueryModel мұрағатынан алынған деректер үлгісінің дұрыс жұмыс істеуі үшін қажет . Және, сәйкесінше, SQL сұрауларын пайдаланады. қолданбада ашылған деректер қоры.
Бұл сынып толығымен болмаса да, Фасад дизайн үлгісін жүзеге асырады, себебі мен айтқанымдай, осы сабақтағы деректер үлгісінде QSqlQueryModel нысандарының бірі пайдаланылады.
НАЗАР АУДАРЫҢЫЗ!!! - дерекқор файлы C:/example қалтасында жасалған, сондықтан DataBase::connectToDataBase() әдісін түзетіңіз немесе 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 "NameDataBase" #define DATABASE_NAME "Name.db" #define TABLE "NameTable" // Название таблицы #define TABLE_FNAME "FisrtName" // Вторая колонка #define TABLE_SNAME "SurName" // Третья колонка #define TABLE_NIK "Nik" // Четвертая колонка // Первая колонка содержит Autoincrement ID class DataBase : public QObject { Q_OBJECT public: explicit DataBase(QObject *parent = 0); ~DataBase(); /* Методы для непосредственной работы с классом * Подключение к базе данных и вставка записей в таблицу * */ void connectToDataBase(); private: // Сам объект базы данных, с которым будет производиться работа QSqlDatabase db; private: /* Внутренние методы для работы с базой данных * */ bool openDataBase(); // Открытие базы данных bool restoreDataBase(); // Восстановление базы данных void closeDataBase(); // Закрытие базы данных bool createTable(); // Создание базы таблицы в базе данных public slots: bool inserIntoTable(const QVariantList &data); // Добавление записей в таблицу bool inserIntoTable(const QString &fname, const QString &sname, const QString &nik); bool removeRecord(const int id); // Удаление записи из таблицы по её id }; #endif // DATABASE_H
database.cpp
Дерекқорға қосылу connectToDataBase() әдісі арқылы инициализацияланады. Кестенің, дерекқордың, файлдың аты, сонымен қатар кестедегі бағандар тақырып файлындағы анықтау директивасында анықталған.
#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()){ // Производим восстановление базы данных return (this->createTable()) ? true : false; } 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_FNAME " VARCHAR(255) NOT NULL," TABLE_SNAME " VARCHAR(255) NOT NULL," TABLE_NIK " 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_FNAME ", " TABLE_SNAME ", " TABLE_NIK " ) " "VALUES (:FName, :SName, :Nik)"); query.bindValue(":FName", data[0].toString()); query.bindValue(":SName", data[1].toString()); query.bindValue(":Nik", data[2].toString()); // После чего выполняется запросом методом exec() if(!query.exec()){ qDebug() << "error insert into " << TABLE; qDebug() << query.lastError().text(); return false; } else { return true; } return false; } /* Второй метод для вставки записи в базу данных * */ bool DataBase::inserIntoTable(const QString &fname, const QString &sname, const QString &nik) { QVariantList data; data.append(fname); data.append(sname); data.append(nik); if(inserIntoTable(data)) return true; else return false; } /* Метод для удаления записи из таблицы * */ bool DataBase::removeRecord(const int id) { // Удаление строки из базы данных будет производитсья с помощью SQL-запроса QSqlQuery query; // Удаление производим по id записи, который передается в качестве аргумента функции query.prepare("DELETE FROM " TABLE " WHERE id= :ID ;"); query.bindValue(":ID", id); // Выполняем удаление if(!query.exec()){ qDebug() << "error delete row " << TABLE; qDebug() << query.lastError().text(); return false; } else { return true; } return false; }
listmodel.h
Деректер үлгісі - data() және roleNames() әдістері қайта анықталатын QSqlQueryModel ішінен мұраланған сынып. Класс сонымен қатар ақпарат көрініске жіберілетін рөлдердің тізімін береді. интерфейсте. Деректерді жою үшін, көріністен берілген рөлге және жол нөміріне сәйкес getID() әдісі арқылы үлгіден шығарылатын жазбаның бірегей ID алуыңыз қажет. Дерекқордан деректерді алу үшін **updateModel() әдісі пайдаланылады, мұнда Дерекқорға SQL сұрауы орнатылады.
#ifndef LISTMODEL_H #define LISTMODEL_H #include <QObject> #include <QSqlQueryModel> class ListModel : public QSqlQueryModel { Q_OBJECT public: /* Перечисляем все роли, которые будут использоваться в TableView * Как видите, они должны лежать в памяти выше параметра Qt::UserRole * Связано с тем, что информация ниже этого адреса не для кастомизаций * */ enum Roles { IdRole = Qt::UserRole + 1, // id FNameRole, // имя SNameRole, // фамилия NikRole // ник }; // объявляем конструктор класса explicit ListModel(QObject *parent = 0); // Переопределяем метод, который будет возвращать данные QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; protected: /* хешированная таблица ролей для колонок. * Метод используется в дебрях базового класса QAbstractItemModel, * от которого наследован класс QSqlQueryModel * */ QHash<int, QByteArray> roleNames() const; signals: public slots: void updateModel(); int getId(int row); }; #endif // LISTMODEL_H
listmodel.cpp
Модельдің жұмысын жүзеге асыру үшін бастапқы код файлына кесте және баған атаулары үшін анықтау директивалары бар database.h тақырып файлын қосамыз. Деректер деректерді көрсету үшін пайдаланылатын TableView ішінде бірдей болуы керек алдын ала анықталған рөлдер арқылы қайтарылады.
#include "listmodel.h" #include "database.h" ListModel::ListModel(QObject *parent) : QSqlQueryModel(parent) { this->updateModel(); } // Метод для получения данных из модели QVariant ListModel::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> ListModel::roleNames() const { /* То есть сохраняем в хеш-таблицу названия ролей * по их номеру * */ QHash<int, QByteArray> roles; roles[IdRole] = "id"; roles[FNameRole] = "fname"; roles[SNameRole] = "sname"; roles[NikRole] = "nik"; return roles; } // Метод обновления таблицы в модели представления данных void ListModel::updateModel() { // Обновление производится SQL-запросом к базе данных this->setQuery("SELECT id, " TABLE_FNAME ", " TABLE_SNAME ", " TABLE_NIK " FROM " TABLE); } // Получение id из строки в модели представления данных int ListModel::getId(int row) { return this->data(this->index(row, 0), IdRole).toInt(); }
main.qml
Qt ішінде үлгі/көрініс/контроллер парадигмасы үлгі/көрініске өзгертілді. Көрініс контроллер мен көріністі біріктіреді. Осылайша, main.qml пайдаланушы енгізетін ақпаратты өңдейді және оны сіңімді түрде серверге жібереді, бірақ деректер базасы класының объектісі QML деңгейінен қол жеткізілетінін және деректер арқылы қосылады. функция ұяшығы, содан кейін шартты түрде контроллер көріністе жүзеге асырылады деп болжауға болады.
import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.1 import QtQuick.Dialogs 1.2 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") // Слой с TaxtField`ами и Button для занесения записей в базу данных RowLayout { id: rowLayout anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.margins: 5 spacing: 10 Text {text: qsTr("Имя")} TextField {id: fnameField} Text {text: qsTr("Фамилия")} TextField { id: snameField} Text {text: qsTr("НИК")} TextField {id: nikField} Button { text: qsTr("Добавить") // Вносим новую запись в базу данных onClicked: { database.inserIntoTable(fnameField.text , snameField.text, nikField.text) myModel.updateModel() // И обновляем модель данных с новой записью } } } TableView { id: tableView anchors.top: rowLayout.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: 5 TableViewColumn { role: "fname" title: "Имя" } TableViewColumn { role: "sname" title: "Фамилия" } TableViewColumn { role: "nik" title: "НИК" } model: myModel // Настройка строки в TableView для перехавата левого клика мыши rowDelegate: Rectangle { anchors.fill: parent color: styleData.selected ? 'skyblue' : (styleData.alternate ? 'whitesmoke' : 'white'); MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { tableView.selection.clear() tableView.selection.select(styleData.row) tableView.currentRow = styleData.row tableView.focus = true switch(mouse.button) { case Qt.RightButton: contextMenu.popup() // Вызываем контексткное меню break default: break } } } } } // Контекстно меню предлагает удаление строки из базы данных Menu { id: contextMenu MenuItem { text: qsTr("Удалить") onTriggered: { /* Вызываем диалоговое окно, * которое уточнит намерение удалить строку из базы данных * */ dialogDelete.open() } } } // Диалог подтверждения удаления строки из базы данных MessageDialog { id: dialogDelete title: qsTr("Удаление записи") text: qsTr("Подтвердите удаление записи из журнала") icon: StandardIcon.Warning standardButtons: StandardButton.Ok | StandardButton.Cancel // При положительном ответе ... onAccepted: { /* ... удаляем строку по id, * который забираем из модели данных * по номеру строки в представлении * */ database.removeRecord(myModel.getId(tableView.currentRow)) myModel.updateModel(); // Обновляем модель данных } } }
Барлығы
Нәтижесінде сіз келесі суретте көрсетілгенге ұқсас қосымшаны аласыз. Сондай-ақ, мен сізге осы мақаладағы бейне оқулықпен танысуды ұсынамын, өйткені ол осы бағдарлама кодына толығырақ ақпарат пен толықтыруларды қамтиды.
Сонымен қатар, мен қасбет дизайнының үлгісі туралы көбірек оқуды және осы үлгінің толық орындалуын келесі мақалада көруді ұсынамын: Фасад дизайны үлгісі
Добрый день, пытаюсь передать текст из QML слоя в C++ при нажатии на кнопку. При нажатии на кнопку выводит ошибку.
что-то мне сдаётся, что здесь просто пересобрать проект нужно с удалением build каталога
Добрый день, подскажите пожалуйста как сделать изменение данных в таблице из запущенного приложения
Добрый день. В статье про это как раз и говорится. Там для этого есть метод inserIntoTable и он в сатье используется.
Добрый день, пытаюсь разобраться и подргнать пример под себя. Есть бд с огромным количеством полей. В приложении на виджетах при использовании QTableView все работает и путем простого sql запроса может вывести сразу всю таблицу. В qml же приложении я так понял жизненно необходимо определять роли. от этого никак не уйти? И как поступать когда ролей порядка 40, а таблиц много.
Второй вопрос : как ваш класс listmodel связывается с бд, т.е. каким образом он понимает , что она существует. проштудировал ваш пример и нигде нет ни ссылки ни намека на класс database, кроме подключения хедера. При попытке воспроизвести ваш класч listview в методе update в дебаге выдает сообщение о том что не удалось открыть бд. Заранее спасибо
Добрый день! можно как то обойтись без метода updateModel()? После вызова этого метода происходит перерисовка страницы(если я правильно понимаю), и все элементы, например, CheckBox перерисовываются и это очень заметно пользователю, либо если элемент имеет свойство скрыть/отобразить, то он принимает свое первоначальное состояние, а если в этом элементе работаешь и он после updateModel() скрывается не очень хорошо, опять разворачивай. Пробывал emit dataChanged(index, index, {role}) но ничего не происходит, модель не обновляет данные, только после updateModel(), происходит обновление модели, хотя в qml я меняю значение модели onClicked: {model.flag = checked;}. Может кто что подскажет?
updateModel() используется для выборки данных из базы данных, поэтому естественно, что если не выбирать данные, то ничего не обновится.
Что касается сохранения скрытого или развёрнутого состояния, то там нужно разбираться с временным сохранением состояния и после использования updateModel() восстанавливать состояние таблицы.
Проблема в том, что просто - это не сделать. Потребуется приличное количество дополнительного кода.
Помогите, пожалуйста. У меня похожая задача, но я в qml слой долен передать не чистый запрос, а со сложной обработкой, поэтому у меня в С++ слое есть иерархия классов, которая имитирует бд и заполняется при включении приложения, но после того, как там всё посчитается, мне нужно отобразить это в qml слое и тут я сломался. Никак не могу понять, как я могу из плюсового слоя наплодить элементов list view в qml слое? Выходит, мне из плюсового слоя нужно как-то заполнять делегат qml слоя и отображать его. Вообще не могу вшарить, как это происходит.
Добрый день. Я тоже присоеденяюсь к вопросу:
[s]"Второй вопрос : как ваш класс listmodel связывается с бд, т.е. каким образом он понимает , что она существует. проштудировал ваш пример и нигде нет ни ссылки ни намека на класс database, кроме подключения хедера. При попытке воспроизвести ваш класч listview в методе update в дебаге выдает сообщение о том что не удалось открыть бд. Заранее спасибо"[/s]
Смотрите, QSqlDatabase имеет статический метод addDatabase("QSQLITE"), который создаёт соединение с базой данных и возвращает инстанс базы данных
Поэтому все дальнейшие манипуляции с базой данных, если инстанс всего один, выполняются автоматически. Qt сам внутри себя хранит подключение к базе данных и выполняет все запросы через созданное соединение. Поэтому все SQL модели уже знают куда обращаться.
А сама инициализация этого подключения в данном кода выполняется в файле main.cpp
Здравствуйте, возникает такая проблема (я новичок):
ApplicationWindow неизвестный элемент. (М300)
для TextField и Button аналогично.
Могу предположить, что из-за более новой версии(я использую 6.7.2) в этой части:
Нужно указывать другие версии, но я не могу найти какие, можете подсказать, пожалуйста?