Evgenii Legotckoi
Evgenii Legotckoi17 августа 2015 г. 2: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

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

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.h

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

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSqlQueryModel>

/* Подключаем заголовочный файл для работы с базой данных */
#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;
    QSqlQueryModel              *modelMain;
    QSqlQueryModel              *modelDevice;

private:
    /* Также присутствуют два метода, которые формируют модель
     * и внешний вид TableView
     * */
    void setupMainModel(const QStringList &headers);
    void setupDeviceModel(const QStringList &headers);
    void createUI();
};

#endif // MAINWINDOW_H

mainwindow.cpp

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

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("QSqlQueryModel Example");
    /* Первым делом необходимо создать объект для работы с базой данных
     * и инициализировать подключение к базе данных
     * */
    db = new DataBase();
    db->connectToDataBase();

    /* После чего производим наполнение таблицы базы данных
     * контентом, который будет отображаться в tableView и tableViewDevice
     * */
    for(int i = 1; i < 4; i++){
        QVariantList data;
        data.append("Device " + QString::number(i));
        data.append("172.168.13." + QString::number(i));
        db->inserIntoDeviceTable(data);
    }

    for(int i = 0; i < 10; i++){
        QVariantList data;
        QString random = QString::number(qrand() % ((4 + 1) - 1) + 1);
        data.append(QDate::currentDate());
        data.append(QTime::currentTime());
        data.append(random);
        db->inserIntoMainTable(data);
    }

    /* Инициализируем модели для представления данных
     * с заданием названий колонок
     * */
    this->setupMainModel(QStringList() << trUtf8("id")
                                       << trUtf8("Дата")
                                       << trUtf8("Время")
                                       << trUtf8("Имя хоста")
                                       << trUtf8("IP адрес")
               );

    this->setupDeviceModel(QStringList() << trUtf8("id")
                                         << trUtf8("Имя хоста")
                                         << trUtf8("IP адрес")
               );
    /* Инициализируем внешний вид таблицы с данными
     * */
    this->createUI();
}

MainWindow::~MainWindow()
{
    delete ui;
}

/* Метод для инициализации модели представления данных
 * */
void MainWindow::setupMainModel(const QStringList &headers)
{
    /* Производим инициализацию модели представления данных
     * */
    modelMain = new QSqlQueryModel(this);

    modelMain->setQuery("SELECT "
                        TABLE ".id, "
                        TABLE "." TABLE_DATE ", "
                        TABLE "." TABLE_TIME ", "
                        DEVICE "." DEVICE_HOSTNAME ", "
                        DEVICE "." DEVICE_IP
                        " FROM " TABLE ", " DEVICE
                        " WHERE " DEVICE ".id = " TABLE "." TABLE_DEVICE_ID
                        " ORDER BY " TABLE "." TABLE_DATE " DESC , " TABLE "." TABLE_TIME " DESC"
                        );

    /* Устанавливаем названия колонок в таблице с сортировкой данных
     * */
    for(int i = 0, j = 0; i < modelMain->columnCount(); i++, j++){
        modelMain->setHeaderData(i,Qt::Horizontal,headers[j]);
    }
}

void MainWindow::setupDeviceModel(const QStringList &headers)
{
    /* Производим инициализацию модели представления данных
     * */
    modelDevice = new QSqlQueryModel(this);

    modelDevice->setQuery("SELECT "
                        DEVICE ".id, "
                        DEVICE "." DEVICE_HOSTNAME ", "
                        DEVICE "." DEVICE_IP
                        " FROM " DEVICE
                        );
    /* Устанавливаем названия колонок в таблице с сортировкой данных
     * */
    for(int i = 0, j = 0; i < modelDevice->columnCount(); i++, j++){
        modelDevice->setHeaderData(i,Qt::Horizontal,headers[j]);
    }
}

void MainWindow::createUI()
{
    ui->tableView->setModel(modelMain);     // Устанавливаем модель на 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);

    ui->tableViewDevice->setModel(modelDevice);     // Устанавливаем модель на TableView
    ui->tableViewDevice->setColumnHidden(0, true);    // Скрываем колонку с id записей
    // Разрешаем выделение строк
    ui->tableViewDevice->setSelectionBehavior(QAbstractItemView::SelectRows);
    // Устанавливаем режим выделения лишь одно строки в таблице
    ui->tableViewDevice->setSelectionMode(QAbstractItemView::SingleSelection);
    // Устанавливаем размер колонок по содержимому
    ui->tableViewDevice->resizeColumnsToContents();
    ui->tableViewDevice->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableViewDevice->horizontalHeader()->setStretchLastSection(true);
}

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

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

database.h

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

/* Директивы имен таблицы, полей таблицы и базы данных */
#define DATABASE_HOSTNAME   "ExampleDataBase"
#define DATABASE_NAME       "DataBase.db"

#define TABLE                   "MainTable"
#define TABLE_DATE              "Date"
#define TABLE_TIME              "Time"
#define TABLE_DEVICE_ID         "DeviceID"

#define DEVICE                  "DeviceTable"
#define DEVICE_IP               "IP"
#define DEVICE_HOSTNAME         "Hostname"

database.cpp

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

/* Метод для создания основной таблицы в базе данных
 * */
bool DataBase::createMainTable()
{
    /* В данном случае используется формирование сырого 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_DEVICE_ID " INTEGER         NOT NULL"
                        " )"
                    )){
        qDebug() << "DataBase: error of create " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

***

/* Метод для вставки записи в основную таблицу
 * */
bool DataBase::inserIntoMainTable(const QVariantList &data)
{
    /* Запрос SQL формируется из QVariantList,
     * в который передаются данные для вставки в таблицу.
     * */
    QSqlQuery query;
    /* В начале SQL запрос формируется с ключами,
     * которые потом связываются методом bindValue
     * для подстановки данных из QVariantList
     * */
    query.prepare("INSERT INTO " TABLE " ( " TABLE_DATE ", "
                                             TABLE_TIME ", "
                                             TABLE_DEVICE_ID " ) "
                  "VALUES (:Date, :Time, :ID )");
    query.bindValue(":Date",        data[0].toDate());
    query.bindValue(":Time",        data[1].toTime());
    query.bindValue(":ID",          data[2].toInt());
    // После чего выполняется запросом методом exec()
    if(!query.exec()){
        qDebug() << "error insert into " << TABLE;
        qDebug() << query.lastError().text();
        return false;
    } else {
        return true;
    }
    return false;
}

Итог

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

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

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

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

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

Evgenii Legotckoi
  • 27 июня 2018 г. 23: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 г. 10:56

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

MainWindow::newCountID(QByteArray refreshValueArr)

{
    QString id11;//Строки значений ID1, ID2 и пришедших значений
    int can1count, can2count;//Значения строк Count of Messages

    int can = refreshValueArr[0];//Значение CAN
    int sizeArr = refreshValueArr.size();

    sizeArr--;

    QByteArray idnew = refreshValueArr.remove(0, 1);//Массив пришедшего ID
    QString strId    = idnew.toHex().toUpper();     //Строка пришедшего ID

    qDebug()<<"can"<<can;
    qDebug()<<"strId****************"<<strId;

    int j = 0, i = 0;

    qDebug()<<"modelMainCAN1->rowCount()"<<modelMainCAN1->rowCount();
    if(modelMainCAN1->rowCount() > 0)//Если значения в строки уже записаны
    {
        for(i; i < modelMainCAN1->rowCount(); i++, qDebug()<<"---i---"<<i)
        {
               id11 = ui->tableViewCAN1->model()->data(//Получаем значения ячеек столбца ID
                           ui->tableViewCAN1->model()->index(i, 0)).value<QString>();
               qDebug()<<"id11------------------- "<<id11;
                if(id11 == strId)//Если строка ID такая же как уже существующая
                {
                    if(i > 0)
                    {
                        can1count = ui->tableViewCAN1->model()->data(//Получаем значения ячеек столбца Message                                                                                                                                                 
                                             ui->tableViewCAN1->model()->index(i, 2)).value<int>();//of count (то, что нужно 
                                                                                                                                          //поменять)

                            can1count++;//Меняем

                            ui->tableViewCAN1->model()->data(//Изменяем значения ячеек столбца Message of count
                            ui->tableViewCAN1->model()->index(i, 2)).setValue(can1count);//Вставляем
                            //Но не вставляется, во всяком случае на виджете не отображается, но сама форма 
                            //начинает тормозить
                        }
                        else//если такого значения нет (первый запуск) его нужно добавить
                        {
                            modelMainCAN1->insertRow(modelMainCAN1->rowCount(QModelIndex()));
                        }
                    }break;//Выходим из цикла
                }
            }


            else//если записей в таблице пока нет (но это второстепенно)
            {
                ui->tableViewCAN1->setModel(modelMainCAN1);
                //modelMainCAN1->insertRow();
            }

        
}
pasagir
  • 25 сентября 2018 г. 11:24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

.h

#include <QMap>
#include <QDebug>
#include <QThread>
#include <QAbstractTableModel>

class MapModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    enum MapRole
    {
        KeyRole,
        ValueRole
    };
    explicit MapModel(QObject *parent = nullptr);
    int rowCount(const QModelIndex& parent = QModelIndex()) const;
    int columnCount(const QModelIndex& parent = QModelIndex()) const;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    inline void setMap(QMap<QString, int>* map){map6 = map;}

private:
    QMap<QString, int>* map;
};

.cpp

#include "mapmodel.h"

MapModel::MapModel(QObject *parent) : QAbstractTableModel(parent)
{
    map = nullptr;
}

int MapModel::rowCount(const QModelIndex& parent) const
{
    if(map)
        return map6->count();
    return 0;
}

int MapModel::columnCount(const QModelIndex& parent) const
{
    return 3;
}

QVariant MapModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
           return QVariant();

    if (orientation == Qt::Horizontal)
    {
        switch (section)
        {
            case 0:
                return QString("ID_Device");

            case 1:
                return QString("Number of Messages");

            default:
                return QVariant();
        }
    }
    return QVariant();
}

QVariant MapModel::data(const QModelIndex& index, int role) const
{
    if(!map)
        return QVariant();
    if(index.row() < 0 || index.row() >= map6->count() || role != Qt::DisplayRole)
    {
        return QVariant();
    }

    if(index.column() == 0)
    {
        return map->keys().at(index.row());
    }
    if(index.column() == 1)
    {
        return map->values().at(index.row());
    }
    return QVariant();
}

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

signalToMain(QByteArray arr);

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

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

Наш слот

void MainWindow::slotFromParser(QByteArray array)
{
    static QMap<QString, int>map1;//Маппер для таблицы 

    QString id11;//Строки значений ID1

    QString strId    = array.toHex().toUpper();     //Строка пришедшего ID

    qDebug()<<"strId****************"<<strId;

    int  rows = 1;//Начальное значение QMap впервые пришедших ключа и значения

    if(map1.contains(strId))//Если контейнер содержит строку(ключ) аналогичную пришедшей
    {
        foreach(QString key, map1.keys()) //Я знаю что foreach лучше не использовать, "горбатого могила"))
        {
            int val = map1.value(key);//Получаем значение соответствующее ключу
            qDebug()<<"val" << val;

            if(strId == key)//Если пришедшая строка равна ключу на определенной позиции
            {
                ++val;//Инкрементируем счетчик
                qDebug()<<"val++1" << val;
                map1.insert(strId, val);//Вставляем в контейнер полученные значения
                break;
            }
        }
     }
     else
     {
         map1.insert(strId, rows);//Вставляем начальное значение
     }
     qDebug()<<"map1------ "<<map1;
     mapMod->setMap(&map1);//Создаем модель
     ui->tableView->setModel(mapMod);//Вставляем модель в таблицу
     ui->tableView->reset();//Перезагружаем таблицу
}

, где mapMod:

mainwindow.h

MapModel   *mapMod;

mainwindow.cpp

mapMod = new MapModel();
pasagir
  • 9 октября 2018 г. 8:10
#include "mapmodel.h"

MapModel::MapModel(QObject *parent) : QAbstractTableModel(parent)
{
    map6 = nullptr;
}

int MapModel::rowCount(const QModelIndex& parent) const
{
    if(map6)
        return map6->count();
    return 0;
}

int MapModel::columnCount(const QModelIndex& parent) const
{
    return 3;
}

QVariant MapModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
           return QVariant();

    if (orientation == Qt::Horizontal)
    {
        switch (section)
        {
            case 0:
                return QString("ID_Device");

            case 1:
                return QString("Number of Messages");

            default:
                return QVariant();
        }
    }
    return QVariant();
}

QVariant MapModel::data(const QModelIndex& index, int role) const
{
    if(!map6)
        return QVariant();
    if(index.row() < 0 || index.row() >= map6->count() || role != Qt::DisplayRole)
    {
        return QVariant();
    }

    if(index.column() == 0)
    {
        return map6->keys().at(index.row());
    }
    if(index.column() == 1)
    {
        return map6->values().at(index.row());
    }
    return QVariant(); 

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

private:
    QMap<QString, int>* map6;



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

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

QSqlQuery::prepare: database not open

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




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

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

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

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

bool DataBase::insertDataIntoDB(QVariantList data)
{
    QSqlQuery query(db);
    QString   str;
    qDebug()<<"InsertInsertInsertInsertInsert"<<QThread::currentThreadId();;

    int count = data.count();
    int N = count/5;
    qDebug()<<"N"<<N;

    str = "INSERT INTO DataBase    ("
                       "DTTable,    "
                       "CANTable,   "
                       "ID_Device,  "
                       "DlcCan,     "
                       "Message    )"
          "VALUES (?, ?, ?, ?, ?)";
    QSqlDatabase::database().transaction();
    for(int i = 0, j = 0; i < N; i++)
    {
        query.prepare(str);
        query.addBindValue(data[j]);
        query.addBindValue(data[++j]);
        query.addBindValue(data[++j]);
        query.addBindValue(data[++j]);
        query.addBindValue(data[++j]);
        query.exec();
        ++j;
    }
    query.exec();
    //qDebug()<<data;

    QSqlDatabase::database().commit();

    if(!query.exec())
    {
        qDebug()<<"Error insert into DataBase";
        qDebug()<<query.lastError().text();
        return false;
    }
    else
    {
        qDebug()<<"Insert compleated";
        return true;
    }
}





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

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

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

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

setQuery(qry);

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

beginResetModel();
endResetModel();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
e
  • ehot
  • 31 марта 2024 г. 11:29

C++ - Тест 003. Условия и циклы

  • Результат:78баллов,
  • Очки рейтинга2
B

C++ - Тест 002. Константы

  • Результат:16баллов,
  • Очки рейтинга-10
B

C++ - Тест 001. Первая программа и типы данных

  • Результат:46баллов,
  • Очки рейтинга-6
Последние комментарии
k
kmssr8 февраля 2024 г. 15:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко4 февраля 2024 г. 22:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 7:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 5:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 декабря 2023 г. 18:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
a
a_vlasov14 апреля 2024 г. 3:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев13 апреля 2024 г. 23:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex4 апреля 2024 г. 1:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
P
Pisych27 февраля 2023 г. 1:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 января 2024 г. 8:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Следите за нами в социальных сетях