Qt ішінде кестелерді құру үшін QSqlQueryModel пайдалану QSqlTableModel немесе QSqlRelationalTableModel пайдаланудан гөрі абстракцияның ең төменгі деңгейімен ең қиын опция болып табылады. Бірақ мұның бәрімен бірге бұл SQL сұрау тілін тереңірек білуді талап ететін ең икемді нұсқа. алдыңғы мақалада екі кесте құрастырылды:
- Негізгі бағандары бар Күні, Уақыты, Хост атауы, IP мекенжайы.
- Хост атауы және IP мекенжайы. бағандары бар құрылғы кестесі**
Негізгі кестеде Хост атауы және IP мекенжайы бағандары Құрылғылар кестесіндегі деректерді ауыстыру үшін пайдаланылған құрылғылардың идентификаторларын көрсетеді. Бұл мақалада бірінші кестенің құрылымы, сондықтан кестелер басқаша көрінеді:
- Негізгі бағандары бар Күні, уақыты, құрылғы идентификаторы.
- Хост атауы және IP мекенжайы. бағандары бар құрылғы кестесі**
Қолданбадағы негізгі кесте SQL сұрауы арқылы жасалады және кестеде сәйкесінше Күн, Уақыт, Хост атауы және IP мекенжайы бағандары болады.
QSqlQueryModel үшін жоба құрылымы
QSqlQueryModel жобасының құрылымы алдыңғы мақаладағы сияқты қалады.
mainwindow.ui
Кестелер де алдыңғы мақаладағыдай пайдаланылады, олардың атауларын еске түсірейік:
- кесте көрінісі
- 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
Бұл бастапқы файлдағы код пен алдыңғы мақаладағы код арасындағы негізгі айырмашылық үлгілерді инициализациялау әдістері setQuery() әдісімен орындалатын SQL сұрауын пайдалануында.
#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 сұранысын қайталау қажет. Бұл төмендегі кодта көрсетілгендей lastQuery() әдісі бар үлгінің query() әдісін шақыру арқылы орындалады:
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; }
Барлығы
Сіздің көрнекі нәтиже алдыңғы сабақта алғаныңыздан ерекшеленбеуі керек. Бұл екі үстелі бар терезе болады.
Какаим образом можно вставить в базу даннных сразу несколько строк? Ситуация такая - на COM-порт приходят данные, непрерывно и поступают в парсер. Там, после обработки записываются в QVariantList (как в примере и далее в БД), но из-за того, что данные идут непрерывно, база данных постоянно занята и обратиться к ней невозможно, пока не отключен порт. Какаим образом в QVariantList можно записать сразу несколько строк, а потом все их отправить в БД?
Тут скорее нужно правильно написать сырой запрос и использовать QSqlQuery
Например, если есть несколько инсертов
То нужно написать соответсвующий запрос
Не уверен, что здесь поможет bind, скорее придётся подготовить аналогичную строку с использованием QVariantList и подать её в QSqlQuery.
Как можно динамически отображать данные в таблице? На COM-порт непрерывно приходят данные, я их принимаю сохраняю в БД, а после остановка приема/передачи данные отображаются в таблице. В таблице, которая нас интересует нужно отображать iD с которых приходят данные и количество пакетов, которое от них пришло. Пытаюсь сделать такvoid
MainWindow::newCountID(QByteArray refreshValueArr)
Это запись метода которая работает параллельно с БД, данные из парсера поступают в БД и в наш метод одновременно
Может вам из того метода кидать сигнал о получении новых данных и делать апдейт таблицы по тому сигналу, чтобы была новая выборка из БД для обновления таблицы?
Сигнал кидайте в конце этого метода, который Вы привели.
Не подскажете как обновить данные в ячейке? Допустим есть строка, состоящая из 2-х столбцов если данные в первом столбце совпадают с пришедшими к значению во втором столбце просто прибавляется 1. Сигнал послал, данные принял, сравнил с данными в столбце №1, а вот значения в ячейке таблицы обновить не получается.
Постоянно обращаться к БД для обновления таблицы не получится, т.к. данные идут непрерывно.
Я так полагаю, что Вам нужно тогда перпроектировать модель данных приложения.
Базу данных используйте только как хранилище, а при динамическом изменении данных, которое у вас имеется используйте модель данных, наследованную от QAbstractItemModel. Там используйте вектор со структурами строки ваших данных. При получении данных, вставляйте запись в БД и в модель данных.
Единственный коннект модели к базе данных будет через сырую выборку SQL-запросом всех необходимых данных, и новое заполнения вектора данных в модели.
Тогда модель данных не будет постоянно обращаться к БД.
При вставке новый строк обычно используют методы beginInsertRows, endInsertRows и т.д, тогда модель будет уведомлять представление об изменениях и Table View будет автоматически перерисовываться. Также метод setData нужно использовать для обновления ячеек в таблице.
Просто то, что вам требуется, это более кастомная модель данных, а не так, которая по видимому у вас используется. То есть QSqlQueryModel здесь уже не очень-то подойдёт.
Тем более, что это ReadOnly модель. Она в своём базовом функционале не позволяет записывать или обновлять данные через себя.
Из разряда "а вдруг кому-нибудь пригодится"
Для динамического обновления таблицы по мере поступления в нее данных я воспользовался контейнером QMap, т.к. у меня есть ID - они выполняют роль ключей и обновляющиеся значения (количество сообщений, которые пришли на этот ID).
Сначала пишем новый класс унаследованный от QAbstractTableMode l:
.h
.cpp
В методе, в котором идет обработка пришедших данных, в нужном месте прописываем какой-нибудь сигнал, который принимает необходимые нам данные, в моем случае это QByteArray
Коннектим наш сигнал со слотом:
Наш слот
, где mapMod:
mainwindow.h
mainwindow.cpp
} не получается подредактировать
В Qt 5.11. при попытке вставить в БД запись выдает ошибку
Попробуйте передать инстанс базы данных в конструктор QSqlQuery
Не получается
Как-то даже странно, а вы что ли в отдельный поток убрали базу данных? То есть изначально инстанс создаётся в одном потоке, а все QSqlQuery в другом потоке? Они должны находиться в одном потоке так-то.
Добрый день.
Хотел спросить вот что. Создал проект на основе QAbstractTableModel. В MainWindow cоответственно создал модель и связал с представлением. Поиск веду по списку элементов модели, потом обнуляю его и копирую в него найденные элементы. Также работает редактирование и удаление для всей строки в диалоговом окне.
Потом изменил класс на QSqlQueryModel - работает, потом на QSqlTableModel - тоже работает, тот же самый проект. Единственное изменение, в выводе всего списка в модель в первых двух последних двух использовал строчку:
а в первой вместо неё:
Возникает вопрос, в чём различие между этими классами? По идее QSqlQueryModel модель для просмотра, но и в этой модели есть возможность редактирования и удаления.
QSqlTableModel выполняет ряд стандартных операций для одной таблицы из базы данных. Поэтому там и реализован функционал по удалению и редактированию.
QSqlQueryModel позволяет выполнить запрос такой хитровыделанности, насколько хватит вашей фантазии, навыков и извращённости. Поэтому она readOnly, поскольку в таком случае невозможно прелоположить, что программисту вообще надо от SQL.
а этот код выполняет нотификацию view о начале и конце обновления таблицы и в принципе не имеет ничего общего с базой данных. Это функционал базовых абстрактных моделей, когда вы пишите кастоные модели и вам требуется в ручную формировать внешний вид представления, часто используется для формирования Tree моделей.
Здарвствуйте, подскажите, как можно добавить еще один стобец в основную таблицу формы, например, повторно вывести столбец IP (дата, время, имя хоста, IP, IP)?
Изменить запрос при создании модели.
Здравствуйте! Подскажите как сделать запрос к базе SQLite с двумя параметрами, в итоге нужно получить не список строк, а только факт наличия строк, соответсвующи именно двум условиям.
В SQL такой запрос работает, но в SQLite допускается использование только одного условия
Как можно обойти это ограничение?
мне кажется у вас просто ошибка в запросе, если у вас реально так написано в коде.
фактически у вас получится такая строка
У вас всё просто сливается в районе даты, не хватает пробелов. Потому что я не вижу проблемы, чтобы подобные запросы не работали в SQLite.
Также посмотрите в сторону оператора COUNT, который вернёт количества.
Получите результат больше 0, значит что-то там есть.
Спасибо за подсказку, получилось. Операция сравнения даты почему-то не выполнялась с использованием переменной, пришлось немного переделать запрос к базе данных.