Evgenii Legotckoi
17 августа 2015 г. 12:55

Qt/C++ - Урок 006. QSqlQueryModel - Таблицы в Qt с помощью SQL-запросов

Использование QSqlQueryModel для формирования таблиц в Qt является наиболее хардкорным вариантом с наиболее низким уровнем абстракции, чем использование QSqlTableModel или QSqlRelationalTableModel . Но при всём при этом и наиболее гибкий вариант, который требует более глубокого знания языка SQL-запросов. В предыдущей статье были построены две таблицы:

  • Основная , в которой были колонки Дата, Время, Имя Хоста, IP-адрес.
  • Таблица устройств, в которой были колонки Имя Хоста и IP-адрес.

В Основной таблице в колонках Имени Хоста и IP-адреса указывались ID-устройств, по которым проводилась подстановка данных из Таблицы устройств. В данной статье структура первой таблицы и следовательно таблицы будут выглядеть иначе:

  • Основная , в которой были колонки Дата, Время, ID устройства.
  • Таблица устройств, в которой были колонки Имя Хоста и IP-адрес.

Генерация Основной таблицы в Приложении будет производиться SQL-запросом и таблица будет иметь соответственно колонки Дата, Время, Имя Хоста и IP-адрес.


Структура проекта для QSqlQueryModel

Структура проекта QSqlQueryModel остается такой же, как в предыдущей статье .

mainwindow.ui

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

  • tableView
  • tableViewDevice

main.cpp

Файл используется в проекте, будучи созданным по умолчанию.

  1. #include "mainwindow.h"
  2. #include <QApplication>
  3.  
  4. int main(int argc, char *argv[])
  5. {
  6. QApplication a(argc, argv);
  7. MainWindow w;
  8. w.show();
  9.  
  10. return a.exec();
  11. }

mainwindow.h

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

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3.  
  4. #include <QMainWindow>
  5. #include <QSqlQueryModel>
  6.  
  7. /* Подключаем заголовочный файл для работы с базой данных */
  8. #include "database.h"
  9.  
  10. namespace Ui {
  11. class MainWindow;
  12. }
  13.  
  14. class MainWindow : public QMainWindow
  15. {
  16. Q_OBJECT
  17.  
  18. public:
  19. explicit MainWindow(QWidget *parent = 0);
  20. ~MainWindow();
  21.  
  22. private:
  23. Ui::MainWindow *ui;
  24. /* В проекте используются объекты для работы с базой данных
  25. * и моделью представления таблицы базы данных
  26. * */
  27. DataBase *db;
  28. QSqlQueryModel *modelMain;
  29. QSqlQueryModel *modelDevice;
  30.  
  31. private:
  32. /* Также присутствуют два метода, которые формируют модель
  33. * и внешний вид TableView
  34. * */
  35. void setupMainModel(const QStringList &headers);
  36. void setupDeviceModel(const QStringList &headers);
  37. void createUI();
  38. };
  39.  
  40. #endif // MAINWINDOW_H

mainwindow.cpp

Главное отличие кода в этом исходном файле от кода из предыдущей статьи заключается в том, что в методах для инициализации моделей используется SQL-запрос, которые выполняется методом setQuery() .

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3.  
  4. MainWindow::MainWindow(QWidget *parent) :
  5. QMainWindow(parent),
  6. ui(new Ui::MainWindow)
  7. {
  8. ui->setupUi(this);
  9. this->setWindowTitle("QSqlQueryModel Example");
  10. /* Первым делом необходимо создать объект для работы с базой данных
  11. * и инициализировать подключение к базе данных
  12. * */
  13. db = new DataBase();
  14. db->connectToDataBase();
  15.  
  16. /* После чего производим наполнение таблицы базы данных
  17. * контентом, который будет отображаться в tableView и tableViewDevice
  18. * */
  19. for(int i = 1; i < 4; i++){
  20. QVariantList data;
  21. data.append("Device " + QString::number(i));
  22. data.append("172.168.13." + QString::number(i));
  23. db->inserIntoDeviceTable(data);
  24. }
  25.  
  26. for(int i = 0; i < 10; i++){
  27. QVariantList data;
  28. QString random = QString::number(qrand() % ((4 + 1) - 1) + 1);
  29. data.append(QDate::currentDate());
  30. data.append(QTime::currentTime());
  31. data.append(random);
  32. db->inserIntoMainTable(data);
  33. }
  34.  
  35. /* Инициализируем модели для представления данных
  36. * с заданием названий колонок
  37. * */
  38. this->setupMainModel(QStringList() << trUtf8("id")
  39. << trUtf8("Дата")
  40. << trUtf8("Время")
  41. << trUtf8("Имя хоста")
  42. << trUtf8("IP адрес")
  43. );
  44.  
  45. this->setupDeviceModel(QStringList() << trUtf8("id")
  46. << trUtf8("Имя хоста")
  47. << trUtf8("IP адрес")
  48. );
  49. /* Инициализируем внешний вид таблицы с данными
  50. * */
  51. this->createUI();
  52. }
  53.  
  54. MainWindow::~MainWindow()
  55. {
  56. delete ui;
  57. }
  58.  
  59. /* Метод для инициализации модели представления данных
  60. * */
  61. void MainWindow::setupMainModel(const QStringList &headers)
  62. {
  63. /* Производим инициализацию модели представления данных
  64. * */
  65. modelMain = new QSqlQueryModel(this);
  66.  
  67. modelMain->setQuery("SELECT "
  68. TABLE ".id, "
  69. TABLE "." TABLE_DATE ", "
  70. TABLE "." TABLE_TIME ", "
  71. DEVICE "." DEVICE_HOSTNAME ", "
  72. DEVICE "." DEVICE_IP
  73. " FROM " TABLE ", " DEVICE
  74. " WHERE " DEVICE ".id = " TABLE "." TABLE_DEVICE_ID
  75. " ORDER BY " TABLE "." TABLE_DATE " DESC , " TABLE "." TABLE_TIME " DESC"
  76. );
  77.  
  78. /* Устанавливаем названия колонок в таблице с сортировкой данных
  79. * */
  80. for(int i = 0, j = 0; i < modelMain->columnCount(); i++, j++){
  81. modelMain->setHeaderData(i,Qt::Horizontal,headers[j]);
  82. }
  83. }
  84.  
  85. void MainWindow::setupDeviceModel(const QStringList &headers)
  86. {
  87. /* Производим инициализацию модели представления данных
  88. * */
  89. modelDevice = new QSqlQueryModel(this);
  90.  
  91. modelDevice->setQuery("SELECT "
  92. DEVICE ".id, "
  93. DEVICE "." DEVICE_HOSTNAME ", "
  94. DEVICE "." DEVICE_IP
  95. " FROM " DEVICE
  96. );
  97. /* Устанавливаем названия колонок в таблице с сортировкой данных
  98. * */
  99. for(int i = 0, j = 0; i < modelDevice->columnCount(); i++, j++){
  100. modelDevice->setHeaderData(i,Qt::Horizontal,headers[j]);
  101. }
  102. }
  103.  
  104. void MainWindow::createUI()
  105. {
  106. ui->tableView->setModel(modelMain); // Устанавливаем модель на TableView
  107. ui->tableView->setColumnHidden(0, true); // Скрываем колонку с id записей
  108. // Разрешаем выделение строк
  109. ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
  110. // Устанавливаем режим выделения лишь одно строки в таблице
  111. ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
  112. // Устанавливаем размер колонок по содержимому
  113. ui->tableView->resizeColumnsToContents();
  114. ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
  115. ui->tableView->horizontalHeader()->setStretchLastSection(true);
  116.  
  117. ui->tableViewDevice->setModel(modelDevice); // Устанавливаем модель на TableView
  118. ui->tableViewDevice->setColumnHidden(0, true); // Скрываем колонку с id записей
  119. // Разрешаем выделение строк
  120. ui->tableViewDevice->setSelectionBehavior(QAbstractItemView::SelectRows);
  121. // Устанавливаем режим выделения лишь одно строки в таблице
  122. ui->tableViewDevice->setSelectionMode(QAbstractItemView::SingleSelection);
  123. // Устанавливаем размер колонок по содержимому
  124. ui->tableViewDevice->resizeColumnsToContents();
  125. ui->tableViewDevice->setEditTriggers(QAbstractItemView::NoEditTriggers);
  126. ui->tableViewDevice->horizontalHeader()->setStretchLastSection(true);
  127. }

Для того, чтобы обновить данные в таблице, необходимо повторить SQL-запрос. Это делается вызовом метода модели query() , у которого применяется метод lastQuery() , как показано в коде ниже:

  1. model->setQuery(modelDevice->query().lastQuery());

database.h

Заголовочный файл для класса, выступающего фасадом для работы с базой данных изменяется всего на две строчки, по сравнению с предыдущей статьей . Изменения приведены в коде ниже:

  1. /* Директивы имен таблицы, полей таблицы и базы данных */
  2. #define DATABASE_HOSTNAME "ExampleDataBase"
  3. #define DATABASE_NAME "DataBase.db"
  4.  
  5. #define TABLE "MainTable"
  6. #define TABLE_DATE "Date"
  7. #define TABLE_TIME "Time"
  8. #define TABLE_DEVICE_ID "DeviceID"
  9.  
  10. #define DEVICE "DeviceTable"
  11. #define DEVICE_IP "IP"
  12. #define DEVICE_HOSTNAME "Hostname"

database.cpp

В данном файле изменению подвергаются лишь два метода, которые работают с Основной таблицей . Из-за того, что изменилось число колонок, которые применены в данной таблице.

  1. /* Метод для создания основной таблицы в базе данных
  2. * */
  3. bool DataBase::createMainTable()
  4. {
  5. /* В данном случае используется формирование сырого SQL-запроса
  6. * с последующим его выполнением.
  7. * */
  8. QSqlQuery query;
  9. if(!query.exec( "CREATE TABLE " TABLE " ("
  10. "id INTEGER PRIMARY KEY AUTOINCREMENT, "
  11. TABLE_DATE " DATE NOT NULL,"
  12. TABLE_TIME " TIME NOT NULL,"
  13. TABLE_DEVICE_ID " INTEGER NOT NULL"
  14. " )"
  15. )){
  16. qDebug() << "DataBase: error of create " << TABLE;
  17. qDebug() << query.lastError().text();
  18. return false;
  19. } else {
  20. return true;
  21. }
  22. return false;
  23. }
  24.  
  25. ***
  26.  
  27. /* Метод для вставки записи в основную таблицу
  28. * */
  29. bool DataBase::inserIntoMainTable(const QVariantList &data)
  30. {
  31. /* Запрос SQL формируется из QVariantList,
  32. * в который передаются данные для вставки в таблицу.
  33. * */
  34. QSqlQuery query;
  35. /* В начале SQL запрос формируется с ключами,
  36. * которые потом связываются методом bindValue
  37. * для подстановки данных из QVariantList
  38. * */
  39. query.prepare("INSERT INTO " TABLE " ( " TABLE_DATE ", "
  40. TABLE_TIME ", "
  41. TABLE_DEVICE_ID " ) "
  42. "VALUES (:Date, :Time, :ID )");
  43. query.bindValue(":Date", data[0].toDate());
  44. query.bindValue(":Time", data[1].toTime());
  45. query.bindValue(":ID", data[2].toInt());
  46. // После чего выполняется запросом методом exec()
  47. if(!query.exec()){
  48. qDebug() << "error insert into " << TABLE;
  49. qDebug() << query.lastError().text();
  50. return false;
  51. } else {
  52. return true;
  53. }
  54. return false;
  55. }

Итог

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

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

pasagir
  • 26 июня 2018 г. 22:34

Какаим образом можно вставить в базу даннных сразу несколько строк? Ситуация такая - на COM-порт приходят данные, непрерывно и поступают в парсер. Там, после обработки записываются в QVariantList (как в примере и далее в БД), но из-за того, что данные идут непрерывно, база данных постоянно занята и обратиться к ней невозможно, пока не отключен порт. Какаим образом в QVariantList можно записать сразу несколько строк, а потом все их отправить в БД?

Evgenii Legotckoi
  • 28 июня 2018 г. 9:02

Тут скорее нужно правильно написать сырой запрос и использовать QSqlQuery

Например, если есть несколько инсертов

INSERT INTO MyTable VALUES ("John", 123, "Lloyds Office");
INSERT INTO MyTable VALUES ("Jane", 124, "Lloyds Office");
INSERT INTO MyTable VALUES ("Billy", 125, "London Office");
INSERT INTO MyTable VALUES ("Miranda", 126, "Bristol Office");

То нужно написать соответсвующий запрос

INSERT INTO table1 (First, Last)
VALUES
    ('Fred', 'Smith'),
    ('John', 'Smith'),
    ('Michael', 'Smith'),
    ('Robert', 'Smith');

Не уверен, что здесь поможет bind, скорее придётся подготовить аналогичную строку с использованием QVariantList и подать её в QSqlQuery.

pasagir
  • 25 сентября 2018 г. 20:56

Как можно динамически отображать данные в таблице? На COM-порт непрерывно приходят данные, я их принимаю сохраняю в БД, а после остановка приема/передачи данные отображаются в таблице. В таблице, которая нас интересует нужно отображать iD с которых приходят данные и количество пакетов, которое от них пришло. Пытаюсь сделать такvoid

MainWindow::newCountID(QByteArray refreshValueArr)

  1. {
  2. QString id11;//Строки значений ID1, ID2 и пришедших значений
  3. int can1count, can2count;//Значения строк Count of Messages
  4.  
  5. int can = refreshValueArr[0];//Значение CAN
  6. int sizeArr = refreshValueArr.size();
  7.  
  8. sizeArr--;
  9.  
  10. QByteArray idnew = refreshValueArr.remove(0, 1);//Массив пришедшего ID
  11. QString strId = idnew.toHex().toUpper(); //Строка пришедшего ID
  12.  
  13. qDebug()<<"can"<<can;
  14. qDebug()<<"strId****************"<<strId;
  15.  
  16. int j = 0, i = 0;
  17.  
  18. qDebug()<<"modelMainCAN1->rowCount()"<<modelMainCAN1->rowCount();
  19. if(modelMainCAN1->rowCount() > 0)//Если значения в строки уже записаны
  20. {
  21. for(i; i < modelMainCAN1->rowCount(); i++, qDebug()<<"---i---"<<i)
  22. {
  23. id11 = ui->tableViewCAN1->model()->data(//Получаем значения ячеек столбца ID
  24. ui->tableViewCAN1->model()->index(i, 0)).value<QString>();
  25. qDebug()<<"id11------------------- "<<id11;
  26. if(id11 == strId)//Если строка ID такая же как уже существующая
  27. {
  28. if(i > 0)
  29. {
  30. can1count = ui->tableViewCAN1->model()->data(//Получаем значения ячеек столбца Message
  31. ui->tableViewCAN1->model()->index(i, 2)).value<int>();//of count (то, что нужно
  32. //поменять)
  33.  
  34. can1count++;//Меняем
  35.  
  36. ui->tableViewCAN1->model()->data(//Изменяем значения ячеек столбца Message of count
  37. ui->tableViewCAN1->model()->index(i, 2)).setValue(can1count);//Вставляем
  38. //Но не вставляется, во всяком случае на виджете не отображается, но сама форма
  39. //начинает тормозить
  40. }
  41. else//если такого значения нет (первый запуск) его нужно добавить
  42. {
  43. modelMainCAN1->insertRow(modelMainCAN1->rowCount(QModelIndex()));
  44. }
  45. }break;//Выходим из цикла
  46. }
  47. }
  48.  
  49.  
  50. else//если записей в таблице пока нет (но это второстепенно)
  51. {
  52. ui->tableViewCAN1->setModel(modelMainCAN1);
  53. //modelMainCAN1->insertRow();
  54. }
  55.  
  56. }
pasagir
  • 25 сентября 2018 г. 21:24

Это запись метода которая работает параллельно с БД, данные из парсера поступают в БД и в наш метод одновременно

Evgenii Legotckoi
  • 26 сентября 2018 г. 16:32

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

Сигнал кидайте в конце этого метода, который Вы привели.

pasagir
  • 27 сентября 2018 г. 14:55

Не подскажете как обновить данные в ячейке? Допустим есть строка, состоящая из 2-х столбцов если данные в первом столбце совпадают с пришедшими к значению во втором столбце просто прибавляется 1. Сигнал послал, данные принял, сравнил с данными в столбце №1, а вот значения в ячейке таблицы обновить не получается.

pasagir
  • 27 сентября 2018 г. 14:58

Постоянно обращаться к БД для обновления таблицы не получится, т.к. данные идут непрерывно.

Evgenii Legotckoi
  • 27 сентября 2018 г. 15:24

Я так полагаю, что Вам нужно тогда перпроектировать модель данных приложения.

Базу данных используйте только как хранилище, а при динамическом изменении данных, которое у вас имеется используйте модель данных, наследованную от QAbstractItemModel. Там используйте вектор со структурами строки ваших данных. При получении данных, вставляйте запись в БД и в модель данных.

Единственный коннект модели к базе данных будет через сырую выборку SQL-запросом всех необходимых данных, и новое заполнения вектора данных в модели.

Тогда модель данных не будет постоянно обращаться к БД.

При вставке новый строк обычно используют методы beginInsertRows, endInsertRows и т.д, тогда модель будет уведомлять представление об изменениях и Table View будет автоматически перерисовываться. Также метод setData нужно использовать для обновления ячеек в таблице.

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

Тем более, что это ReadOnly модель. Она в своём базовом функционале не позволяет записывать или обновлять данные через себя.


pasagir
  • 27 сентября 2018 г. 15:37
ReadOnly - теперь ясно. Спасибо, ща буду переписывать.
pasagir
  • 9 октября 2018 г. 18:05

Из разряда "а вдруг кому-нибудь пригодится"

Для динамического обновления таблицы по мере поступления в нее данных я воспользовался контейнером QMap, т.к. у меня есть ID - они выполняют роль ключей и обновляющиеся значения (количество сообщений, которые пришли на этот ID).

Сначала пишем новый класс унаследованный от QAbstractTableMode l:

.h

  1. #include <QMap>
  2. #include <QDebug>
  3. #include <QThread>
  4. #include <QAbstractTableModel>
  5.  
  6. class MapModel : public QAbstractTableModel
  7. {
  8. Q_OBJECT
  9.  
  10. public:
  11. enum MapRole
  12. {
  13. KeyRole,
  14. ValueRole
  15. };
  16. explicit MapModel(QObject *parent = nullptr);
  17. int rowCount(const QModelIndex& parent = QModelIndex()) const;
  18. int columnCount(const QModelIndex& parent = QModelIndex()) const;
  19. QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
  20. QVariant headerData(int section, Qt::Orientation orientation, int role) const;
  21. inline void setMap(QMap<QString, int>* map){map6 = map;}
  22.  
  23. private:
  24. QMap<QString, int>* map;
  25. };

.cpp

  1. #include "mapmodel.h"
  2.  
  3. MapModel::MapModel(QObject *parent) : QAbstractTableModel(parent)
  4. {
  5. map = nullptr;
  6. }
  7.  
  8. int MapModel::rowCount(const QModelIndex& parent) const
  9. {
  10. if(map)
  11. return map6->count();
  12. return 0;
  13. }
  14.  
  15. int MapModel::columnCount(const QModelIndex& parent) const
  16. {
  17. return 3;
  18. }
  19.  
  20. QVariant MapModel::headerData(int section, Qt::Orientation orientation, int role) const
  21. {
  22. if (role != Qt::DisplayRole)
  23. return QVariant();
  24.  
  25. if (orientation == Qt::Horizontal)
  26. {
  27. switch (section)
  28. {
  29. case 0:
  30. return QString("ID_Device");
  31.  
  32. case 1:
  33. return QString("Number of Messages");
  34.  
  35. default:
  36. return QVariant();
  37. }
  38. }
  39. return QVariant();
  40. }
  41.  
  42. QVariant MapModel::data(const QModelIndex& index, int role) const
  43. {
  44. if(!map)
  45. return QVariant();
  46. if(index.row() < 0 || index.row() >= map6->count() || role != Qt::DisplayRole)
  47. {
  48. return QVariant();
  49. }
  50.  
  51. if(index.column() == 0)
  52. {
  53. return map->keys().at(index.row());
  54. }
  55. if(index.column() == 1)
  56. {
  57. return map->values().at(index.row());
  58. }
  59. return QVariant();
  60. }

В методе, в котором идет обработка пришедших данных, в нужном месте прописываем какой-нибудь сигнал, который принимает необходимые нам данные, в моем случае это QByteArray

  1. signalToMain(QByteArray arr);

Коннектим наш сигнал со слотом:

  1. connect(parserClass, SIGNAL(signalToModelMain(QByteArray)), this, SLOT(slotFromParser(QByteArray)));

Наш слот

  1. void MainWindow::slotFromParser(QByteArray array)
  2. {
  3. static QMap<QString, int>map1;//Маппер для таблицы
  4.  
  5. QString id11;//Строки значений ID1
  6.  
  7. QString strId = array.toHex().toUpper(); //Строка пришедшего ID
  8.  
  9. qDebug()<<"strId****************"<<strId;
  10.  
  11. int rows = 1;//Начальное значение QMap впервые пришедших ключа и значения
  12.  
  13. if(map1.contains(strId))//Если контейнер содержит строку(ключ) аналогичную пришедшей
  14. {
  15. foreach(QString key, map1.keys()) //Я знаю что foreach лучше не использовать, "горбатого могила"))
  16. {
  17. int val = map1.value(key);//Получаем значение соответствующее ключу
  18. qDebug()<<"val" << val;
  19.  
  20. if(strId == key)//Если пришедшая строка равна ключу на определенной позиции
  21. {
  22. ++val;//Инкрементируем счетчик
  23. qDebug()<<"val++1" << val;
  24. map1.insert(strId, val);//Вставляем в контейнер полученные значения
  25. break;
  26. }
  27. }
  28. }
  29. else
  30. {
  31. map1.insert(strId, rows);//Вставляем начальное значение
  32. }
  33. qDebug()<<"map1------ "<<map1;
  34. mapMod->setMap(&map1);//Создаем модель
  35. ui->tableView->setModel(mapMod);//Вставляем модель в таблицу
  36. ui->tableView->reset();//Перезагружаем таблицу
  37. }

, где mapMod:

mainwindow.h

  1. MapModel *mapMod;

mainwindow.cpp

  1. mapMod = new MapModel();
pasagir
  • 9 октября 2018 г. 18:10
  1. #include "mapmodel.h"
  2.  
  3. MapModel::MapModel(QObject *parent) : QAbstractTableModel(parent)
  4. {
  5. map6 = nullptr;
  6. }
  7.  
  8. int MapModel::rowCount(const QModelIndex& parent) const
  9. {
  10. if(map6)
  11. return map6->count();
  12. return 0;
  13. }
  14.  
  15. int MapModel::columnCount(const QModelIndex& parent) const
  16. {
  17. return 3;
  18. }
  19.  
  20. QVariant MapModel::headerData(int section, Qt::Orientation orientation, int role) const
  21. {
  22. if (role != Qt::DisplayRole)
  23. return QVariant();
  24.  
  25. if (orientation == Qt::Horizontal)
  26. {
  27. switch (section)
  28. {
  29. case 0:
  30. return QString("ID_Device");
  31.  
  32. case 1:
  33. return QString("Number of Messages");
  34.  
  35. default:
  36. return QVariant();
  37. }
  38. }
  39. return QVariant();
  40. }
  41.  
  42. QVariant MapModel::data(const QModelIndex& index, int role) const
  43. {
  44. if(!map6)
  45. return QVariant();
  46. if(index.row() < 0 || index.row() >= map6->count() || role != Qt::DisplayRole)
  47. {
  48. return QVariant();
  49. }
  50.  
  51. if(index.column() == 0)
  52. {
  53. return map6->keys().at(index.row());
  54. }
  55. if(index.column() == 1)
  56. {
  57. return map6->values().at(index.row());
  58. }
  59. return QVariant(); 

} не получается подредактировать

  1. private:
  2. QMap<QString, int>* map6;



pasagir
  • 16 октября 2018 г. 22:14

В Qt 5.11. при попытке вставить в БД запись выдает ошибку

  1. QSqlQuery::prepare: database not open
  2.  
  3. QSqlDatabasePrivate::database: requested database does not belong to the calling thread.
  1. как с этим бороться?




Evgenii Legotckoi
  • 17 октября 2018 г. 13:09

Попробуйте передать инстанс базы данных в конструктор QSqlQuery

  1. QSqlQuery q(db);
pasagir
  • 17 октября 2018 г. 14:43

Не получается

  1. bool DataBase::insertDataIntoDB(QVariantList data)
  2. {
  3. QSqlQuery query(db);
  4. QString str;
  5. qDebug()<<"InsertInsertInsertInsertInsert"<<QThread::currentThreadId();;
  6.  
  7. int count = data.count();
  8. int N = count/5;
  9. qDebug()<<"N"<<N;
  10.  
  11. str = "INSERT INTO DataBase ("
  12. "DTTable, "
  13. "CANTable, "
  14. "ID_Device, "
  15. "DlcCan, "
  16. "Message )"
  17. "VALUES (?, ?, ?, ?, ?)";
  18. QSqlDatabase::database().transaction();
  19. for(int i = 0, j = 0; i < N; i++)
  20. {
  21. query.prepare(str);
  22. query.addBindValue(data[j]);
  23. query.addBindValue(data[++j]);
  24. query.addBindValue(data[++j]);
  25. query.addBindValue(data[++j]);
  26. query.addBindValue(data[++j]);
  27. query.exec();
  28. ++j;
  29. }
  30. query.exec();
  31. //qDebug()<<data;
  32.  
  33. QSqlDatabase::database().commit();
  34.  
  35. if(!query.exec())
  36. {
  37. qDebug()<<"Error insert into DataBase";
  38. qDebug()<<query.lastError().text();
  39. return false;
  40. }
  41. else
  42. {
  43. qDebug()<<"Insert compleated";
  44. return true;
  45. }
  1. }





Evgenii Legotckoi
  • 18 октября 2018 г. 14:31

Как-то даже странно, а вы что ли в отдельный поток убрали базу данных? То есть изначально инстанс создаётся в одном потоке, а все QSqlQuery в другом потоке? Они должны находиться в одном потоке так-то.

VB
  • 24 сентября 2020 г. 15:47
  • (ред.)

Добрый день.
Хотел спросить вот что. Создал проект на основе QAbstractTableModel. В MainWindow cоответственно создал модель и связал с представлением. Поиск веду по списку элементов модели, потом обнуляю его и копирую в него найденные элементы. Также работает редактирование и удаление для всей строки в диалоговом окне.
Потом изменил класс на QSqlQueryModel - работает, потом на QSqlTableModel - тоже работает, тот же самый проект. Единственное изменение, в выводе всего списка в модель в первых двух последних двух использовал строчку:

  1. setQuery(qry);

а в первой вместо неё:

  1. beginResetModel();
  2. endResetModel();

Возникает вопрос, в чём различие между этими классами? По идее QSqlQueryModel модель для просмотра, но и в этой модели есть возможность редактирования и удаления.

Evgenii Legotckoi
  • 25 сентября 2020 г. 12:40

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

  1. setQuery - выполняет запрос к базе данных

а этот код выполняет нотификацию view о начале и конце обновления таблицы и в принципе не имеет ничего общего с базой данных. Это функционал базовых абстрактных моделей, когда вы пишите кастоные модели и вам требуется в ручную формировать внешний вид представления, часто используется для формирования Tree моделей.

  1. beginResetModel();
  2. endResetModel();
f
  • 26 мая 2021 г. 12:11

Здарвствуйте, подскажите, как можно добавить еще один стобец в основную таблицу формы, например, повторно вывести столбец IP (дата, время, имя хоста, IP, IP)?

Ruslan Polupan
  • 2 июня 2021 г. 19:21

Изменить запрос при создании модели.

A
  • 28 мая 2022 г. 15:37

Здравствуйте! Подскажите как сделать запрос к базе SQLite с двумя параметрами, в итоге нужно получить не список строк, а только факт наличия строк, соответсвующи именно двум условиям.
В SQL такой запрос работает, но в SQLite допускается использование только одного условия

  1. QString SET_DATE = QDate::currentDate().toString("yyyy-MM-dd");
  2. QSqlQuery query;
  3. query.exec("SELECT * FROM dokument WHERE d_isp <" + SET_DATE + "AND isp = 0");

Как можно обойти это ограничение?

Evgenii Legotckoi
  • 30 мая 2022 г. 20:31

мне кажется у вас просто ошибка в запросе, если у вас реально так написано в коде.
фактически у вас получится такая строка

  1. "SELECT * FROM dokument WHERE d_isp <yyyy-MM-ddAND isp = 0")

У вас всё просто сливается в районе даты, не хватает пробелов. Потому что я не вижу проблемы, чтобы подобные запросы не работали в SQLite.

Также посмотрите в сторону оператора COUNT, который вернёт количества.

  1. "SELECT COUNT(*) FROM dokument WHERE d_isp < yyyy-MM-dd AND isp = 0"

Получите результат больше 0, значит что-то там есть.

A
  • 31 мая 2022 г. 10:30

Спасибо за подсказку, получилось. Операция сравнения даты почему-то не выполнялась с использованием переменной, пришлось немного переделать запрос к базе данных.

  1. QSqlQuery query("SELECT count(*) FROM dokument WHERE isp = 0 AND d_isp < date('now','localtime')");
  2. while (query.next()) {
  3. QString country = query.value(0).toString();
  4. }

Комментарии

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