Evgenii Legotckoi
Evgenii Legotckoi17 серпня 2015 р. 02: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());

база даних.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 р. 06:32

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

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

pasagir
  • 27 вересня 2018 р. 04:55

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

pasagir
  • 27 вересня 2018 р. 04:58

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

Evgenii Legotckoi
  • 27 вересня 2018 р. 05:24

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

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

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

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

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

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

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


pasagir
  • 27 вересня 2018 р. 05:37
ReadOnly - теперь ясно. Спасибо, ща буду переписывать.
pasagir
  • 09 жовтня 2018 р. 08: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
  • 09 жовтня 2018 р. 08: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 р. 03:09

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

QSqlQuery q(db);
pasagir
  • 17 жовтня 2018 р. 04: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 р. 04:31

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

VB
  • 24 вересня 2020 р. 05:47
  • (відредаговано)

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

setQuery(qry);

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

beginResetModel();
endResetModel();

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

Evgenii Legotckoi
  • 25 вересня 2020 р. 02:40

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

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

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

beginResetModel();
endResetModel();
f
  • 26 травня 2021 р. 02:11

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

Ruslan Polupan
  • 02 червня 2021 р. 09:21

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

A
  • 28 травня 2022 р. 05: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 р. 00: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();
        }

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах