Евгений Легоцкой17 августа 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 хостинг.
Поддержать автора Donate

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

Тут скорее нужно правильно написать сырой запрос и использовать 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.

Как можно динамически отображать данные в таблице? На 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();
            }

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

Для динамического обновления таблицы по мере поступления в нее данных я воспользовался контейнером 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();
#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;



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

QSqlQuery::prepare: database not open

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




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

QSqlQuery q(db);

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

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;
    }
}





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

VB

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

setQuery(qry);

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

beginResetModel();
endResetModel();

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

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

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

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

beginResetModel();
endResetModel();

Комментарии

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

Позвольте мне порекомендовать вам отличный хостинг, на котором расположен EVILEG.

В течение многих лет Timeweb доказывает свою стабильность.

Для проектов на Django рекомендую VDS хостинг

Посмотреть Хостинг
СП

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

  • Результат:93баллов,
  • Очки рейтинга8
VS

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

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

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

  • Результат:93баллов,
  • Очки рейтинга8
Последние комментарии

Qt/C++ - Урок 074. Генерация псевдослучайных чисел с использованием случайной библиотеки STD

А использование функции global() не решает ли эти проблемы? value = QRandomGenerator::global()->bounded(15, 43); Получаемая последовательность каждый раз новая.

Qt/C++ - Урок 074. Генерация псевдослучайных чисел с использованием случайной библиотеки STD

А использование функции global() не решает ли эти проблемы? value = QRandomGenerator::global()->bounded(15, 43); Получаемая последовательность каждый раз новая.
S

QML - Урок 026. Intents с Qt для Android, часть 1

Есть ли возможность приведения java типа у QAndroidJniObject? Интересует конкретно class to
ВК

Qt/C++ - Урок 015. QTableWidget или Как сделать таблицу с чекбоксами

Кто-нибудь знает, как сделать так, чтобы в QTableWidget состоящей из чекбоксов в строке таблицы можно было выбрать только один checkbox ?

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

QSqlTableModel выполняет ряд стандартных операций для одной таблицы из базы данных. Поэтому там и реализован функционал по удалению и редактированию. QSqlQueryModel позволяет выполнить запр…
Сейчас обсуждают на форуме

Не проверять форму если нажали кнопку

Возможно, если загуглить ошибку "could not be changed because the data didn't validate", можно было бы найти страницу, которая говорит, что надо вызвать метод "is_valid()" у формы? у вас же вали…
  • Nomad
  • 1 октября 2020 г. 5:22

MyForm(forms.Form): - непонятка

понятно спасибо
S

QWebView android

На android не запускается, иначе я бы не создавал этот пост. Собственно, вопрос я решил сам, там ещё понадобилось setDomStorageEnabled(true) вызвать.

не могу передать стринг с QLineEdit

QLineEdit *myLineEdit = new QLineEdit("line edit name", this); QString str = myLineEdit->text();

Siganal slot в ui

Добрый день, Не совсем понял, какой код должен находиться в слоте, но можно подключиться через лямбда функцию. connect(timer, &QTimer::timeout, this, [](){ ui->scrollArea-&g;…
О нас
Услуги
© EVILEG 2015-2020
Рекомендует хостинг TIMEWEB