Evgenii Legotckoi
24 ноября 2015 г. 20:11

QML - Урок 016. База данных SQLite и работа с ней в QML Qt

Небольшой пример по работе с базой данных в QML Qt . В данном уроке сводится воедино информация о применении сигналов и слотов в QML Qt , о доступе к C++ классам из QML слоя, реализации интерфейса приложения на QML, а также о реализации модели/представлении для таблицы базы данных.

База данных содержит таблицу со списком людей, в которой имеется четыре колонки:

  • id (INTEGER) - уникальный номер записи;
  • FirstName (VARCHAR (255)) - Имя;
  • SurName (VARCHAR (255)) - Фамилия;
  • Nik (VARCHAR (255)) - Ник.

Приложение должно реализовывать удаление и добавление записей в базу данных через интерфейс приложения. Для добавления записей в Базу данных будет использоваться три поля для ввода данных и кнопка, которая инициализирует добавление данных в базу данных SQL . Добавление записей в таблицу осуществляется через класс обёртку предназначенным для этого методом.Также в приложении присутствует класс ListModel , который реализует модель представления данных для отображения информации в TableView в слое QML.


Структура проекта

  • QmlDataBase.pro - профайл проекта;
  • database.h - заголовочный файл класса-обёртки для работы с базой данных;
  • database.cpp - файл исходных кодов класса-обёртки для работы с базой данных;
  • listmodel.h - заголовочный файл модели данных;
  • listmodel.cpp - файл исходных кодов модели данных;
  • main.cpp - основной файл исходных кодов приложения;
  • main.qml - основной файл qml.

QmlDataBase.pro

Для работы с базой данных необходимо подключить модуль sql, а также модуль widgets для нативного внешнего вида приложения.

  1. TEMPLATE = app
  2.  
  3. QT += qml quick widgets sql
  4.  
  5. SOURCES += main.cpp \
  6. database.cpp \
  7. listmodel.cpp
  8.  
  9. RESOURCES += qml.qrc
  10.  
  11. # Additional import path used to resolve QML modules in Qt Creator's code model
  12. QML_IMPORT_PATH =
  13.  
  14. # Default rules for deployment.
  15. include(deployment.pri)
  16.  
  17. HEADERS += \
  18. database.h \
  19. listmodel.h

main.cpp

В основном файле исходных кодов подключаем класс-обёртку для работы с базой данных и класс модели данных. Объекты этих классов необходимо объявить и инициализировать в данном файле, а также настроить доступ к этим объектам и их свойствам из QML слоя.

После того, как доступ к объектам настроен, из QML слоя становятся доступны следующие свойства и функции объекта, которые объявлены в его классе в качестве:

  1. Сигналов
  2. Слотов
  3. А также функции, которые фигурируют в макросе Q_PROPERTY
  1. #include <QApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QQmlContext>
  4.  
  5. #include "database.h"
  6. #include "listmodel.h"
  7.  
  8. int main(int argc, char *argv[])
  9. {
  10. QApplication app(argc, argv);
  11. QQmlApplicationEngine engine;
  12.  
  13. // Подключаемся к базе данных
  14. DataBase database;
  15. database.connectToDataBase();
  16.  
  17. // Объявляем и инициализируем модель данных
  18. ListModel *model = new ListModel();
  19.  
  20. // Обеспечиваем доступ к модели и классу для работы с базой данных из QML
  21. engine.rootContext()->setContextProperty("myModel", model);
  22. engine.rootContext()->setContextProperty("database", &database);
  23.  
  24. engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
  25.  
  26. return app.exec();
  27. }

database.h

В данном классе объявлены методы для работы с базой данных:

  • методы для подключения к базе данных, её восстановления;
  • методы для добавления записи в базу данных;
  • методы для удаления записей в таблице данных.

Подключение к базе данных необходимо для правильной работы модели данных, которая наследована от QSqlQueryModel . И соответственно использует SQL запросы к открытой в приложении базы данных.

Данный класс реализует паттерн проектирования Facade, хотя и не полностью, поскольку как я уже сказал, что одна из сущностей QSqlQueryModel используется в модели данных в этом уроке.

ВНИМАНИЕ!!! - файл базы данных создается в папке C:/example , поэтому или поправьте метод DataBase::connectToDataBase() или создайте папку example на диске C .

  1. #ifndef DATABASE_H
  2. #define DATABASE_H
  3.  
  4. #include <QObject>
  5. #include <QSql>
  6. #include <QSqlQuery>
  7. #include <QSqlError>
  8. #include <QSqlDatabase>
  9. #include <QFile>
  10. #include <QDate>
  11. #include <QDebug>
  12.  
  13. /* Директивы имен таблицы, полей таблицы и базы данных */
  14. #define DATABASE_HOSTNAME "NameDataBase"
  15. #define DATABASE_NAME "Name.db"
  16.  
  17. #define TABLE "NameTable" // Название таблицы
  18. #define TABLE_FNAME "FisrtName" // Вторая колонка
  19. #define TABLE_SNAME "SurName" // Третья колонка
  20. #define TABLE_NIK "Nik" // Четвертая колонка
  21.  
  22. // Первая колонка содержит Autoincrement ID
  23.  
  24. class DataBase : public QObject
  25. {
  26. Q_OBJECT
  27. public:
  28. explicit DataBase(QObject *parent = 0);
  29. ~DataBase();
  30. /* Методы для непосредственной работы с классом
  31. * Подключение к базе данных и вставка записей в таблицу
  32. * */
  33. void connectToDataBase();
  34.  
  35. private:
  36. // Сам объект базы данных, с которым будет производиться работа
  37. QSqlDatabase db;
  38.  
  39. private:
  40. /* Внутренние методы для работы с базой данных
  41. * */
  42. bool openDataBase(); // Открытие базы данных
  43. bool restoreDataBase(); // Восстановление базы данных
  44. void closeDataBase(); // Закрытие базы данных
  45. bool createTable(); // Создание базы таблицы в базе данных
  46.  
  47. public slots:
  48. bool inserIntoTable(const QVariantList &data); // Добавление записей в таблицу
  49. bool inserIntoTable(const QString &fname, const QString &sname, const QString &nik);
  50. bool removeRecord(const int id); // Удаление записи из таблицы по её id
  51. };
  52.  
  53. #endif // DATABASE_H

database.cpp

Инициализация подключения к базе данных производится методом connectToDataBase() . Название таблицы, базы данных, файла, а также колонок в таблице определены в директивах define в заголовочном файле.

  1. #include "database.h"
  2.  
  3. DataBase::DataBase(QObject *parent) : QObject(parent)
  4. {
  5.  
  6. }
  7.  
  8. DataBase::~DataBase()
  9. {
  10.  
  11. }
  12.  
  13. /* Методы для подключения к базе данных
  14. * */
  15. void DataBase::connectToDataBase()
  16. {
  17. /* Перед подключением к базе данных производим проверку на её существование.
  18. * В зависимости от результата производим открытие базы данных или её восстановление
  19. * */
  20. if(!QFile("C:/example/" DATABASE_NAME).exists()){
  21. this->restoreDataBase();
  22. } else {
  23. this->openDataBase();
  24. }
  25. }
  26.  
  27. /* Методы восстановления базы данных
  28. * */
  29. bool DataBase::restoreDataBase()
  30. {
  31. // Если база данных открылась ...
  32. if(this->openDataBase()){
  33. // Производим восстановление базы данных
  34. return (this->createTable()) ? true : false;
  35. } else {
  36. qDebug() << "Не удалось восстановить базу данных";
  37. return false;
  38. }
  39. return false;
  40. }
  41.  
  42. /* Метод для открытия базы данных
  43. * */
  44. bool DataBase::openDataBase()
  45. {
  46. /* База данных открывается по заданному пути
  47. * и имени базы данных, если она существует
  48. * */
  49. db = QSqlDatabase::addDatabase("QSQLITE");
  50. db.setHostName(DATABASE_HOSTNAME);
  51. db.setDatabaseName("C:/example/" DATABASE_NAME);
  52. if(db.open()){
  53. return true;
  54. } else {
  55. return false;
  56. }
  57. }
  58.  
  59. /* Методы закрытия базы данных
  60. * */
  61. void DataBase::closeDataBase()
  62. {
  63. db.close();
  64. }
  65.  
  66. /* Метод для создания таблицы в базе данных
  67. * */
  68. bool DataBase::createTable()
  69. {
  70. /* В данном случае используется формирование сырого SQL-запроса
  71. * с последующим его выполнением.
  72. * */
  73. QSqlQuery query;
  74. if(!query.exec( "CREATE TABLE " TABLE " ("
  75. "id INTEGER PRIMARY KEY AUTOINCREMENT, "
  76. TABLE_FNAME " VARCHAR(255) NOT NULL,"
  77. TABLE_SNAME " VARCHAR(255) NOT NULL,"
  78. TABLE_NIK " VARCHAR(255) NOT NULL"
  79. " )"
  80. )){
  81. qDebug() << "DataBase: error of create " << TABLE;
  82. qDebug() << query.lastError().text();
  83. return false;
  84. } else {
  85. return true;
  86. }
  87. return false;
  88. }
  89.  
  90. /* Метод для вставки записи в базу данных
  91. * */
  92. bool DataBase::inserIntoTable(const QVariantList &data)
  93. {
  94. /* Запрос SQL формируется из QVariantList,
  95. * в который передаются данные для вставки в таблицу.
  96. * */
  97. QSqlQuery query;
  98. /* В начале SQL запрос формируется с ключами,
  99. * которые потом связываются методом bindValue
  100. * для подстановки данных из QVariantList
  101. * */
  102. query.prepare("INSERT INTO " TABLE " ( " TABLE_FNAME ", "
  103. TABLE_SNAME ", "
  104. TABLE_NIK " ) "
  105. "VALUES (:FName, :SName, :Nik)");
  106. query.bindValue(":FName", data[0].toString());
  107. query.bindValue(":SName", data[1].toString());
  108. query.bindValue(":Nik", data[2].toString());
  109.  
  110. // После чего выполняется запросом методом exec()
  111. if(!query.exec()){
  112. qDebug() << "error insert into " << TABLE;
  113. qDebug() << query.lastError().text();
  114. return false;
  115. } else {
  116. return true;
  117. }
  118. return false;
  119. }
  120.  
  121. /* Второй метод для вставки записи в базу данных
  122. * */
  123. bool DataBase::inserIntoTable(const QString &fname, const QString &sname, const QString &nik)
  124. {
  125. QVariantList data;
  126. data.append(fname);
  127. data.append(sname);
  128. data.append(nik);
  129.  
  130. if(inserIntoTable(data))
  131. return true;
  132. else
  133. return false;
  134. }
  135.  
  136. /* Метод для удаления записи из таблицы
  137. * */
  138. bool DataBase::removeRecord(const int id)
  139. {
  140. // Удаление строки из базы данных будет производитсья с помощью SQL-запроса
  141. QSqlQuery query;
  142.  
  143. // Удаление производим по id записи, который передается в качестве аргумента функции
  144. query.prepare("DELETE FROM " TABLE " WHERE id= :ID ;");
  145. query.bindValue(":ID", id);
  146.  
  147. // Выполняем удаление
  148. if(!query.exec()){
  149. qDebug() << "error delete row " << TABLE;
  150. qDebug() << query.lastError().text();
  151. return false;
  152. } else {
  153. return true;
  154. }
  155. return false;
  156. }

listmodel.h

Модель данных является классом, наследованным от QSqlQueryModel, в котором переопределены методы data() и roleNames(). Также в классе перечислены роли, по которым передается информация в представление в интерфейсе. Для удаления данных необходимо получать уникальный ID записи, который вытягивается из модели по методом getID() по роли и номеру строки, который был передан из представления. Для получения данных из Базы данных используется метод updateModel(), в котором устанавливается SQL-запрос к Базе данных.

  1. #ifndef LISTMODEL_H
  2. #define LISTMODEL_H
  3.  
  4. #include <QObject>
  5. #include <QSqlQueryModel>
  6.  
  7. class ListModel : public QSqlQueryModel
  8. {
  9. Q_OBJECT
  10. public:
  11. /* Перечисляем все роли, которые будут использоваться в TableView
  12. * Как видите, они должны лежать в памяти выше параметра Qt::UserRole
  13. * Связано с тем, что информация ниже этого адреса не для кастомизаций
  14. * */
  15. enum Roles {
  16. IdRole = Qt::UserRole + 1, // id
  17. FNameRole, // имя
  18. SNameRole, // фамилия
  19. NikRole // ник
  20. };
  21.  
  22. // объявляем конструктор класса
  23. explicit ListModel(QObject *parent = 0);
  24.  
  25. // Переопределяем метод, который будет возвращать данные
  26. QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
  27.  
  28. protected:
  29. /* хешированная таблица ролей для колонок.
  30. * Метод используется в дебрях базового класса QAbstractItemModel,
  31. * от которого наследован класс QSqlQueryModel
  32. * */
  33. QHash<int, QByteArray> roleNames() const;
  34.  
  35. signals:
  36.  
  37. public slots:
  38. void updateModel();
  39. int getId(int row);
  40. };
  41.  
  42. #endif // LISTMODEL_H

listmodel.cpp

Для реализации работы модели в файл исходных кодов подключаем заголовочный файл database.h, в котором имеются директивы define для названий таблицы и колонок. Возвращение данных производится по предопределенным ролям, которые должны быть идентичными в TableView, который будет использован для отображения данных.

  1. #include "listmodel.h"
  2. #include "database.h"
  3.  
  4. ListModel::ListModel(QObject *parent) :
  5. QSqlQueryModel(parent)
  6. {
  7. this->updateModel();
  8. }
  9.  
  10. // Метод для получения данных из модели
  11. QVariant ListModel::data(const QModelIndex & index, int role) const {
  12.  
  13. // Определяем номер колонки, адрес так сказать, по номеру роли
  14. int columnId = role - Qt::UserRole - 1;
  15. // Создаём индекс с помощью новоиспечённого ID колонки
  16. QModelIndex modelIndex = this->index(index.row(), columnId);
  17.  
  18. /* И с помощью уже метода data() базового класса
  19. * вытаскиваем данные для таблицы из модели
  20. * */
  21. return QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
  22. }
  23.  
  24. // Метод для получения имен ролей через хешированную таблицу.
  25. QHash<int, QByteArray> ListModel::roleNames() const {
  26. /* То есть сохраняем в хеш-таблицу названия ролей
  27. * по их номеру
  28. * */
  29. QHash<int, QByteArray> roles;
  30. roles[IdRole] = "id";
  31. roles[FNameRole] = "fname";
  32. roles[SNameRole] = "sname";
  33. roles[NikRole] = "nik";
  34. return roles;
  35. }
  36.  
  37. // Метод обновления таблицы в модели представления данных
  38. void ListModel::updateModel()
  39. {
  40. // Обновление производится SQL-запросом к базе данных
  41. this->setQuery("SELECT id, " TABLE_FNAME ", " TABLE_SNAME ", " TABLE_NIK " FROM " TABLE);
  42. }
  43.  
  44. // Получение id из строки в модели представления данных
  45. int ListModel::getId(int row)
  46. {
  47. return this->data(this->index(row, 0), IdRole).toInt();
  48. }

main.qml

В Qt парадигма модели/вида/контроллера изменена на модель/представление. Представление объединяет в себе контроллер и вид. Таким образом, main.qml обрабатывает информацию, которую вводит пользователь и передаёт её в backend в удобоваримом виде, но если учесть, что к объекту класса database дан доступ из QML слоя, а добавление данных производится через функцию слот, то условно можно принять, что контроллер реализован в представлении.

  1. import QtQuick 2.5
  2. import QtQuick.Controls 1.4
  3. import QtQuick.Layouts 1.1
  4. import QtQuick.Dialogs 1.2
  5.  
  6. ApplicationWindow {
  7. visible: true
  8. width: 640
  9. height: 480
  10. title: qsTr("Hello World")
  11.  
  12. // Слой с TaxtField`ами и Button для занесения записей в базу данных
  13. RowLayout {
  14. id: rowLayout
  15. anchors.top: parent.top
  16. anchors.left: parent.left
  17. anchors.right: parent.right
  18. anchors.margins: 5
  19.  
  20. spacing: 10
  21.  
  22. Text {text: qsTr("Имя")}
  23. TextField {id: fnameField}
  24. Text {text: qsTr("Фамилия")}
  25. TextField { id: snameField}
  26. Text {text: qsTr("НИК")}
  27. TextField {id: nikField}
  28.  
  29. Button {
  30. text: qsTr("Добавить")
  31.  
  32. // Вносим новую запись в базу данных
  33. onClicked: {
  34. database.inserIntoTable(fnameField.text , snameField.text, nikField.text)
  35. myModel.updateModel() // И обновляем модель данных с новой записью
  36. }
  37. }
  38. }
  39.  
  40. TableView {
  41. id: tableView
  42. anchors.top: rowLayout.bottom
  43. anchors.left: parent.left
  44. anchors.right: parent.right
  45. anchors.bottom: parent.bottom
  46. anchors.margins: 5
  47.  
  48. TableViewColumn {
  49. role: "fname"
  50. title: "Имя"
  51. }
  52. TableViewColumn {
  53. role: "sname"
  54. title: "Фамилия"
  55. }
  56. TableViewColumn {
  57. role: "nik"
  58. title: "НИК"
  59. }
  60.  
  61. model: myModel
  62.  
  63. // Настройка строки в TableView для перехавата левого клика мыши
  64. rowDelegate: Rectangle {
  65. anchors.fill: parent
  66. color: styleData.selected ? 'skyblue' : (styleData.alternate ? 'whitesmoke' : 'white');
  67. MouseArea {
  68. anchors.fill: parent
  69. acceptedButtons: Qt.RightButton | Qt.LeftButton
  70. onClicked: {
  71. tableView.selection.clear()
  72. tableView.selection.select(styleData.row)
  73. tableView.currentRow = styleData.row
  74. tableView.focus = true
  75.  
  76. switch(mouse.button) {
  77. case Qt.RightButton:
  78. contextMenu.popup() // Вызываем контексткное меню
  79. break
  80. default:
  81. break
  82. }
  83. }
  84. }
  85. }
  86. }
  87.  
  88. // Контекстно меню предлагает удаление строки из базы данных
  89. Menu {
  90. id: contextMenu
  91.  
  92. MenuItem {
  93. text: qsTr("Удалить")
  94. onTriggered: {
  95. /* Вызываем диалоговое окно,
  96. * которое уточнит намерение удалить строку из базы данных
  97. * */
  98. dialogDelete.open()
  99. }
  100. }
  101. }
  102.  
  103. // Диалог подтверждения удаления строки из базы данных
  104. MessageDialog {
  105. id: dialogDelete
  106. title: qsTr("Удаление записи")
  107. text: qsTr("Подтвердите удаление записи из журнала")
  108. icon: StandardIcon.Warning
  109. standardButtons: StandardButton.Ok | StandardButton.Cancel
  110.  
  111. // При положительном ответе ...
  112. onAccepted: {
  113. /* ... удаляем строку по id,
  114. * который забираем из модели данных
  115. * по номеру строки в представлении
  116. * */
  117. database.removeRecord(myModel.getId(tableView.currentRow))
  118. myModel.updateModel(); // Обновляем модель данных
  119. }
  120. }
  121. }

Итог

В результате Вы получите приложение, которое будет выглядеть так, как показано на ниже следующем рисунке. Также рекомендую к ознакомлению видеоурок по данной статье, поскольку в нём имеется более подробная информация и дополнения по данному программному коду.

Кроме этого рекомендую почитать подробнее о паттерне проектирования Facade и посмотреть более полный вариант реализации данного паттерна в следующей статье: Шаблон проектирования "Фасад"

Видеоурок

Вам это нравится? Поделитесь в социальных сетях!

Terabaytus
  • 5 июня 2018 г. 17:01
Добрый день, запустил ваш пример при нажатии на кнопку получаю вот эту ошибку

ASSERT failure in QList<T>::operator[]: "index out of range", file ../../Qt5.11.0/5.11.0/gcc_64/include/QtCore/qlist.h, line 545

Подскажите пожалуйста в чём может быть дело на что она указывает где искать ?
Terabaytus
  • 6 июня 2018 г. 13:28
Разобрался, не правильное добавление записи в БД а именно лишний запрос query.bindValue стоял в методе inserIntoTable.
W
  • 15 июня 2018 г. 20:08

Добрый день, пытаюсь передать текст из QML слоя в C++ при нажатии на кнопку. При нажатии на кнопку выводит ошибку.


TypeError: Property 'remove_db' of object [object Object] is not a function
Не могу понять в чем ошибка(новичок в Qt) Если есть возможно можно простой пример как передать текст или число из QML лося в C++ или где ошибка в моем коде. Спасибо заранее)
QML:

import QtQuick 2.0
import Sailfish.Silica 1.0
import QtPositioning 5.3
import QtLocation 5.0
import QtQuick 2.0
import QtWebKit 3.0
import MyModule 1.0

Page{
    id: pageMenu
    GPSCoor{
        id:data
        // @disable-check M16
        onSendToTime:
        {
            myModel.append({"time" : data_time_real_time})
        }
    }

    Rectangle{
        id: kv_menu
        anchors.fill: parent                                                 //Привязка к радителю
        color: "steelblue"
        ListView
        {
            id: myListView
            anchors
            {
                top: kv_menu.top
                right: kv_menu.right
                left: kv_menu.left
                bottom: buttonHistory.top

                rightMargin: 30
                leftMargin: 30
                topMargin: 30
                bottomMargin: 100
            }

            model: myModel
            delegate: Item{
                id:itemrec
                width: myListView.width
                height: 90

                Button{
                    id: rectest
                    anchors.margins: 6
                    anchors.fill: parent

                    Text
                    {
                        id:textNuj
                        objectName: "textZae"
                        color: "white"
                        font.pixelSize: 40
                        text: time + " - " + (model.index+1)
                        anchors.horizontalCenter: rectest.horizontalCenter
                        anchors.verticalCenter: rectest.verticalCenter
                    }
                    onClicked:
                    {
//                        console.log("text - " + text)
//                        console.log("modelN - "+ model.index)
//                        console.log("textNuj - " + textNuj.text)
//                        //pageStack.push(dialog)
                        data.remove_db(model.index);
                    }
                }
            }
        }
        ListModel
        {
            id:myModel
        }
        Button
        {
            id: buttonHistory
            height: 90
            anchors{
                right: kv_menu.right
                left: kv_menu.left
                bottom: kv_menu.bottom

                rightMargin: 30
                leftMargin: 30
                bottomMargin: 30
            }
            Label{
                color: "white"
                text: "Показать треки"
                anchors.horizontalCenter: buttonHistory.horizontalCenter
                anchors.verticalCenter: buttonHistory.verticalCenter
            }
            onClicked: {
                data.pageMapX()
            }
        }
    }
    Component{
        id: dialog
        Dialog {
            id: winDialog
            property string name

            Rectangle{
                id:winDialogRec1
                color: "steelblue"
                anchors.fill: parent
                property string name
                DialogHeader
                {   id: dialogHeader
                    acceptText: qsTr("Принять")
                    cancelText: qsTr("Отменить")
                }
                Rectangle{
                    id:winDialogRec
                    color: "steelblue"
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.top: parent.top
                    anchors.topMargin: 400

                    Label{
                        id:label1
                        anchors{
                           horizontalCenter: winDialogRec.horizontalCenter
                           top: parent.top
                        }
                        color: "white"
                        text: "Вы действительно хотите удалить трек?"
                    }
                    Label{
                        id: label2
                        anchors{
                            horizontalCenter: winDialogRec.horizontalCenter
                            top: parent.top

                            topMargin: 250

                        }
                        color: "white"
                        text: name
                    }
                    Button{
                        width: parent / 2 - 45
                        anchors{
                            top: parent.top
                            left: parent.left
                            topMargin: 120
                            leftMargin: 30
                        }
                        text: "Нет"
                        onClicked:
                        {
                           label2.text = "Нет, не удалять этот трек"
                        }
                    }
                    Button{

                        width: parent / 2 - 45
                        anchors{
                            top: parent.top
                            right: parent.right
                            topMargin: 120
                            rightMargin: 30
                        }

                        text: "Да"
                        onClicked:
                        {
                           label2.text = "Да, удалить этот трек"
                         }
                    }

                }

            }

        }
    }
}
Заголовок:
#ifndef GPSCOOR_H
#define GPSCOOR_H
#include <QObject>
#include <QDebug>

class GPSCoor: public QObject
{
    Q_OBJECT
    Q_PROPERTY(double x_cor READ  getSomePropertyX   WRITE setSomePropertyX   NOTIFY sendToQml)     //Для обменна даными с переменной x_cor
    Q_PROPERTY(double y_cor READ  getSomePropertyY   WRITE setSomePropertyY   NOTIFY sendToQml)     //Для обменна даными с переменной y_cor
    Q_PROPERTY(QString way   READ  getSomePropertyWay WRITE setSomePropertyWay NOTIFY sendToWay)     //Для обменна даными о времени t
    Q_PROPERTY(QString data_time_real_time  READ  getSomePropertyTime   WRITE setSomePropertyTime   NOTIFY sendToTime)     //Для обменна даными о расстоянии m

    //    Q_PROPERTY(double arreyXY READ  getSomePropertyX WRITE setSomePropertyX NOTIFY sendToQml)     //Для обменна даными с переменной x_cor

public:
    explicit GPSCoor(QObject *parent = 0);
    Q_INVOKABLE void remove_db();

    double getSomePropertyX()const;                                                             //Для обменна даными с переменной x_cor
    double getSomePropertyY()const;                                                             //Для обменна даными с переменной y_cor
    QString getSomePropertyTime()const;                                                          //Для обменна даными о времени t
    QString getSomePropertyWay()const;                                                           //Для обменна даными о расстоянии m

    void setSomePropertyY(const double &);                                                      //Для обменна даными с переменной y_cor
    void setSomePropertyX(const double &);                                                      //Для обменна даными с переменной x_cor
    void setSomePropertyTime(const QString &);                                                   //Для обменна даными о времени t
    void setSomePropertyWay(const QString &);                                                    //Для обменна даными о расстоянии m

    void creat_db();                                                                            //Для создания базы данных и записи данных
    void insert_db();                                                                           //Для извлечения данных и бызы
    void distance();

    //Для выисления растояния от точки до точки
signals:
    void sendToQml(double);                                                                     //Сигнал для передачи данных в qml
    void sendToWay(QString);
    void sendToTime(QString);

public slots:

    void reciveX();                                                                             //Сигнал для получения данных с qml(FirstPage)
    void reciveTime();                                                                          //Сигнал для получения данных о времени
    //   void reciveWay();
    void pageMapX();                                                                            //Сигнал для получения данных с qml(Map)


private:
    QString data_time_real_time;
    QString way;
    double x_cor;                                                                               //Перменная координата х_cor
    double y_cor;                                                                               //Перменная координата y_cor
    int data_time;                                                                              //Перменная для измерении времени

    double Way_point=0;

    float CMX11 = 0;
    float CMY11 = 0;
    float CMX21 = 0;
    float CMY21 = 0;
    const float pi = 3.141592653589793;
    const float radius = 6378.16;
    double num =  1;                                                                               //Перменная счетчик для цыкла
    double n = 1000;
    double z = 2;
    //    double arreyXY[2][1000];

};
#endif // GPSCOOR_H
С++:
#include "gpscoor.h"
#include <QObject>
#include <QDebug>
#include "QtSql/QSqlDatabase"
#include "QSqlQuery"
#include <QtSql>
#include <QTime>
#include <QLocale>
#include <QDateTime>
#include <QtMath>
#include <math.h>

GPSCoor::GPSCoor(QObject *parent): QObject(parent)
{


}

void GPSCoor::remove_db()
{

    qDebug()<<"remuve_db() = "<<data_time_real_time<<endl;
    QSqlQuery query;                                                     //Осуществляем запрос
    // Удаление строки из базы данных будет производитсья с помощью SQL-запроса


       // Удаление производим по id записи, который передается в качестве аргумента функции
       query.prepare("DELETE FROM my_ta WHERE data_time= :data_time;");
       query.bindValue(":data_time", data_time_real_time);

       // Выполняем удаление
       if(!query.exec()){
           qDebug() << "error delete row " << "my_ta";
           qDebug() << query.lastError().text();

       } else
       {

       }

    //    query.exec("SELECT id, data_time ,x_cor, y_cor FROM my_ta ");
//    while (query.next())                                                 //Выводим значения из запроса
//    {
//        int id = query.value(0).toInt();                                 //Получаю данные из БД
//        data_time = query.value(1).toInt();                          //Получаю данные из БД
//        x_cor = query.value(2).toDouble();                               //Получаю данные из БД
//        y_cor = query.value(3).toDouble();                               //Получаю данные из БД
//        QString data_time_2 = QString::number(data_time);
//        if(data_time_real_time == data_time_2)
//        {
//            query.exec("DELETE FROM jobs WHERE data_time = ?");
//            query.addBindValue(data_time);
//            qDebug()<<"Delete for BD good"<<endl;
//        }
//        else{
//            qDebug()<<"Error Delete for BD bad"<<endl;
//        }
//    }

}

void GPSCoor::reciveTime()
{
   emit sendToTime(data_time_real_time);
}

double GPSCoor::getSomePropertyX()const                 //Для обменна даными с переменной x_cor
{
    return x_cor;
}

double GPSCoor::getSomePropertyY()const                 //Для обменна даными с переменной y_cor
{    
    return y_cor;
}

QString GPSCoor::getSomePropertyWay()const                 //Для обменна даными с переменной way
{
    return way;
}

QString GPSCoor::getSomePropertyTime()const                 //Для обменна даными с переменной time
{
    return data_time_real_time;
}

void GPSCoor::setSomePropertyX(const double &i)         //Для обменна даными с переменной x_cor
{
    x_cor = i;
    emit sendToQml(x_cor);
}

void GPSCoor::setSomePropertyY(const double &i)         //Для обменна даными с переменной y_cor
{
    y_cor = i;
    emit sendToQml(y_cor);
}

void GPSCoor::setSomePropertyWay(const QString &i)         //Для обменна даными с переменной way
{
    way = i;
    emit sendToWay(way);
}

void GPSCoor::setSomePropertyTime(const QString &i)         //Для обменна даными с переменной time
{
    data_time_real_time = i;
    emit sendToTime(data_time_real_time);
}

void GPSCoor::reciveX()                                 //Принимаю данные из QML(FirstPage)
{
    emit sendToQml(x_cor);                              //Принимаю данные в QML(FirstPage)
    emit sendToQml(y_cor);                              //Принимаю данные в QML(FirstPage)
    emit creat_db();                                    //Для создания и записи БД на стороне С++
    //emit insert_db();                                 //Для извлечения данных и бызы на стороне С++
}

void GPSCoor::pageMapX()                                //Принимаю данные из QML(Map)
{
    emit insert_db();                                   //Для извлечения данных и бызы на стороне С++
}

void GPSCoor::creat_db()                                //Создаем SQLite
{
    QTime time = QTime::currentTime();                  //Создаю обект для получения реальное время
    data_time = time.msecsSinceStartOfDay();            //Записываю время в переменную в формате миллисекунд
    qDebug()<<data_time<<endl;

    QSqlDatabase dbase = QSqlDatabase::addDatabase("QSQLITE");          //Создая обект для QSQLite
    dbase.setDatabaseName("test_2.sqlite");                             //Создаю БД
    if (!dbase.open()) {                                                //Проверяю БД
        qDebug() << "Error db";
    }else{
        qDebug()<<"Good db";
    }

    QSqlQuery a_query;
    // DDL query
    QString str = "CREATE TABLE my_ta ("               //создаю таблицу
            "id integer PRIMARY KEY NOT NULL, "
            "data_time integer,"
            "x_cor double, "
            "y_cor double"
            ");";
    bool b = a_query.exec(str);                        //Проверка на создания
    if (!b) {
        qDebug() << "error2!";
    }
    else{
        qDebug()<<"send2";
    }

    //        QString str_insrt = "INSERT INTO my_ta (data_time,x_cor,y_cor) VALUES (%1, %2, %3);";             // Записываю данные в Таблицу
    //        str = str_insrt.arg(data_time)
    //                .arg(x_cor)
    //                .arg(y_cor);
    //        b = a_query.exec(str);
    //        if (!b) {
    //            qDebug() << "error3";
    //        }else{
    //            qDebug()<<"Send3";
    //        }
}

void GPSCoor::insert_db()                               //Вывести данные из db и отправить в map.qml
{

    QSqlQuery query;                                                     //Осуществляем запрос
    query.exec("SELECT id, data_time ,x_cor, y_cor FROM my_ta ");
    while (query.next())                                                 //Выводим значения из запроса
    {

        int id = query.value(0).toInt();                                 //Получаю данные из БД
        data_time = query.value(1).toInt();                              //Получаю данные из БД
        x_cor = query.value(2).toDouble();                               //Получаю данные из БД
        y_cor = query.value(3).toDouble();                               //Получаю данные из БД

        if(id == num)
        {
            emit sendToQml(x_cor);                                      //Отправляю данные в QML(Map)
            emit sendToQml(y_cor);                                      //Отправляю данные в QML(Map)

            QTime time = QTime::fromMSecsSinceStartOfDay(data_time);
            data_time_real_time = time.toString("hh:mm:ss"); //str = "20:04:23.003"
            emit sendToTime(data_time_real_time);

            qDebug()<< "id - "<< id << endl;                            //простая проерка данных, вывожу на экран
            qDebug()<< "data_time_real_time - "<< data_time_real_time << endl;              //простая проерка данных, вывожу на экран
            qDebug()<< "y_cor - "<< x_cor << endl;
            qDebug()<< "x_cor - "<< y_cor << endl;
            //Вычисление растояния между 2 координатами гипатенуза
            CMX11 = y_cor;
            CMY11 = x_cor;
            if(id==1)
            {
//                CMX21 = y_cor;
//                CMY21 = x_cor;
            }
            else{
               emit distance();

                qDebug()<<"Way (км)  "<< Way_point << endl;
            }
            CMX21 = CMX11;
            CMY21 = CMY11;

        }
        else
        {
        }

    }
    num += 20;                       //Увеличиваю num++
}
void GPSCoor::distance(){
    Way_point = round((Way_point + sqrt(pow((CMX21 - CMX11),2) + pow((CMY21 - CMY11),2))*100)*100)/100;
    way = QString::number(Way_point);
    emit sendToWay(way);
}
Evgenii Legotckoi
  • 18 июня 2018 г. 13:01

что-то мне сдаётся, что здесь просто пересобрать проект нужно с удалением build каталога

ММ
  • 1 мая 2020 г. 15:44

Добрый день, подскажите пожалуйста как сделать изменение данных в таблице из запущенного приложения

Добрый день. В статье про это как раз и говорится. Там для этого есть метод inserIntoTable и он в сатье используется.

D:
  • 6 августа 2020 г. 19:14
  • (ред.)

Добрый день, пытаюсь разобраться и подргнать пример под себя. Есть бд с огромным количеством полей. В приложении на виджетах при использовании QTableView все работает и путем простого sql запроса может вывести сразу всю таблицу. В qml же приложении я так понял жизненно необходимо определять роли. от этого никак не уйти? И как поступать когда ролей порядка 40, а таблиц много.
Второй вопрос : как ваш класс listmodel связывается с бд, т.е. каким образом он понимает , что она существует. проштудировал ваш пример и нигде нет ни ссылки ни намека на класс database, кроме подключения хедера. При попытке воспроизвести ваш класч listview в методе update в дебаге выдает сообщение о том что не удалось открыть бд. Заранее спасибо

РГ
  • 10 августа 2020 г. 19:33
  • (ред.)

Добрый день! можно как то обойтись без метода updateModel()? После вызова этого метода происходит перерисовка страницы(если я правильно понимаю), и все элементы, например, CheckBox перерисовываются и это очень заметно пользователю, либо если элемент имеет свойство скрыть/отобразить, то он принимает свое первоначальное состояние, а если в этом элементе работаешь и он после updateModel() скрывается не очень хорошо, опять разворачивай. Пробывал emit dataChanged(index, index, {role}) но ничего не происходит, модель не обновляет данные, только после updateModel(), происходит обновление модели, хотя в qml я меняю значение модели onClicked: {model.flag = checked;}. Может кто что подскажет?

Evgenii Legotckoi
  • 20 августа 2020 г. 16:13

updateModel() используется для выборки данных из базы данных, поэтому естественно, что если не выбирать данные, то ничего не обновится.
Что касается сохранения скрытого или развёрнутого состояния, то там нужно разбираться с временным сохранением состояния и после использования updateModel() восстанавливать состояние таблицы.
Проблема в том, что просто - это не сделать. Потребуется приличное количество дополнительного кода.

ВР
  • 18 ноября 2020 г. 2:31

Помогите, пожалуйста. У меня похожая задача, но я в qml слой долен передать не чистый запрос, а со сложной обработкой, поэтому у меня в С++ слое есть иерархия классов, которая имитирует бд и заполняется при включении приложения, но после того, как там всё посчитается, мне нужно отобразить это в qml слое и тут я сломался. Никак не могу понять, как я могу из плюсового слоя наплодить элементов list view в qml слое? Выходит, мне из плюсового слоя нужно как-то заполнять делегат qml слоя и отображать его. Вообще не могу вшарить, как это происходит.

juvf
  • 6 марта 2023 г. 13:44

Добрый день. Я тоже присоеденяюсь к вопросу:
[s]"Второй вопрос : как ваш класс listmodel связывается с бд, т.е. каким образом он понимает , что она существует. проштудировал ваш пример и нигде нет ни ссылки ни намека на класс database, кроме подключения хедера. При попытке воспроизвести ваш класч listview в методе update в дебаге выдает сообщение о том что не удалось открыть бд. Заранее спасибо"[/s]

Evgenii Legotckoi
  • 13 марта 2023 г. 18:13

Смотрите, QSqlDatabase имеет статический метод addDatabase("QSQLITE"), который создаёт соединение с базой данных и возвращает инстанс базы данных

  1. QSqlDatabase dbase = QSqlDatabase::addDatabase("QSQLITE");

Поэтому все дальнейшие манипуляции с базой данных, если инстанс всего один, выполняются автоматически. Qt сам внутри себя хранит подключение к базе данных и выполняет все запросы через созданное соединение. Поэтому все SQL модели уже знают куда обращаться.

А сама инициализация этого подключения в данном кода выполняется в файле main.cpp

  1. #include <QApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QQmlContext>
  4.  
  5. #include "database.h"
  6. #include "listmodel.h"
  7.  
  8. int main(int argc, char *argv[])
  9. {
  10. QApplication app(argc, argv);
  11. QQmlApplicationEngine engine;
  12.  
  13. // Подключаемся к базе данных
  14. DataBase database;
  15. database.connectToDataBase();
  16.  
  17. // Объявляем и инициализируем модель данных
  18. ListModel *model = new ListModel();
  19.  
  20. // Обеспечиваем доступ к модели и классу для работы с базой данных из QML
  21. engine.rootContext()->setContextProperty("myModel", model);
  22. engine.rootContext()->setContextProperty("database", &database);
  23.  
  24. engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
  25.  
  26. return app.exec();
  27. }
d
  • 5 июля 2024 г. 20:02
  • (ред.)

Здравствуйте, возникает такая проблема (я новичок):
ApplicationWindow неизвестный элемент. (М300)
для TextField и Button аналогично.
Могу предположить, что из-за более новой версии(я использую 6.7.2) в этой части:

  1. import QtQuick 2.5
  2. import QtQuick.Controls 1.4
  3. import QtQuick.Layouts 1.1
  4. import QtQuick.Dialogs 1.2

Нужно указывать другие версии, но я не могу найти какие, можете подсказать, пожалуйста?

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь