Es war notwendig, eine große Datenmenge aus der Datenbank für die spätere Arbeit des Benutzers mit ihnen zu erhalten (Warenverzeichnis in einem bestimmten Geschäft).
Für den Code treten nicht viel. Programmieren ist für mich ein Hobby als Denksport.
Die Abfrage selbst dauert sehr lange. Deshalb machen wir es in einem Flow, wir zeigen dem Benutzer einen endlosen Fortschrittsbalken mit dem Satz „Lehnen Sie sich in ihrem Stuhl zurück und warten Sie.“ :-)
Die in der Anfrage empfangenen Daten werden in einem Vektor von Objekten der Artikelklasse gespeichert.
Artikel
Klasse zum Speichern eines Datensatzes der resultierenden Abfrage. Wir verwenden einen Vektor, um alle Datensätze zu speichern.
Artikel.h
#ifndef ARTICLES_H #define ARTICLES_H #include <QString> class Articles { public: Articles(); void setColParam(int _colParam); void setID(int _articleID); void setShortName(QString _shortName); void setAmount(float _amount); void setPrice(float _price); int getColParam(); int getID(); QString getShortName(); float getAmount(); float getPrice(); private: int colParam; // Количетсво параметров. необходимое для последующего создания модели int artileID; // Код товара QString shortname; // Наименование float amount; // Количество float price; // Цена }; #endif // ARTICLES_H
Artikel.cpp
#include "articles.h" Articles::Articles() { setColParam(4); } void Articles::setColParam(int _colParam) { colParam = _colParam; } int Articles::getColParam() { return colParam; } void Articles::setID(int _articleID) { artileID = _articleID; } void Articles::setShortName(QString _shortName) { shortname = _shortName; } void Articles::setAmount(float _amount) { amount = _amount; } void Articles::setPrice(float _price) { price = _price; } int Articles::getID() { return artileID; } QString Articles::getShortName() { return shortname; } float Articles::getAmount() { return amount; } float Articles::getPrice() { return price; }
Zum Protokollieren und Debuggen habe ich die in diesem Artikel Protokollieren von Qt-Anwendungsereignissen in einer Textdatei beschriebenen Methoden verwendet.
Artikelliste
Klasse zum Abrufen aus der Datenbank
listarticles.h
#ifndef LISTARTICLES_H #define LISTARTICLES_H #include "articles.h" #include <QObject> #include <QSqlQuery> #include <QSqlError> #include <QVector> #include <QSqlRecord> class ListArticles : public QObject { Q_OBJECT public: explicit ListArticles(QSqlRecord rec, int terminal_D, int shift, QObject *parent = nullptr); signals: // Сигнал дя отправки данных в основной поток для дальнейшей их обработки void signalSendArticlesList(QVector<Articles>); // Сигнал об окончании выборки данных void finish(); public slots: // Слот для начала выбоки данных, вызываемый из основного потока void createListGoods(); private: int m_terminalID; // Номер магазина, параметр для запроса int m_shiftID; // Номер смены, параметр для запроса QVector<Articles> goods; // Вектор в котором будем хранить результаты запроса и передадим в основной поток QSqlRecord connRec; // Запись выбранная из другой модели содержащщая параметры подключения к базе данных }; #endif // LISTARTICLES_H
listarticles.cpp
#include "listarticles.h" #include "loggingcategories.h" ListArticles::ListArticles(QSqlRecord rec, int terminal_D, int shift, QObject *parent) : QObject(parent) { // Получаем входные данные m_terminalID = terminal_D; m_shiftID = shift; connRec =rec; } void ListArticles::createListGoods() { // Создаем объект для хранения одной записи Articles ar; // регистрируем тип для передачи его через механизм сигнал слот. typedef QVector<Articles> vek; qRegisterMetaType<vek>("vektor"); // Из Асистента // "Соединение может использоваться только внутри создавшего его потока. Перемещение соединений между потоками и создание запросов в другой поток не поддерживается. // Поэтому создаем новое соединение QSqlDatabase dbth = QSqlDatabase::addDatabase("QIBASE","thcentr"); dbth.setHostName(connRec.value("conn_host").toString()); dbth.setDatabaseName(connRec.value("conn_db").toString()); dbth.setUserName(connRec.value("conn_user").toString()); dbth.setPassword(connRec.value("conn_pass").toString()); if(!dbth.open()) { qCritical(logCritical()) << Q_FUNC_INFO << "Не могу подключится к центральной базе" << endl << dbth.lastError().text(); return; } // Привязыем запрос к соединению QSqlQuery q = QSqlQuery(dbth); // Формируем строку с запросом. // Такого типа запросы предпочитаю создавать ввиде строки и тестировать их в непосредственно в менеджере базы данных QString strSQL = QString("SELECT A.GARTICLE_ID, GA.SHORTNAME, SL.AMOUNT, " "(SELECT FIRST 1 NEWPRICE FROM HISTORY_PRICES HP " "WHERE HP.TERMINAL_ID = SL.TERMINAL_ID " "AND HP.GARTICLE_ID = A.GARTICLE_ID " "AND HP.DATEOP < SH.DATCLOSE " "ORDER BY HP.DATEOP DESC) AS PRICE " "FROM GET_ASALDOS (%1, %2, NULL, 0) AS SL " "INNER JOIN ARTICLES A ON A.TERMINAL_ID = SL.TERMINAL_ID AND A.ARTICLE_ID = SL.ARTICLE_ID " "LEFT JOIN SHIFTS SH ON SH.TERMINAL_ID = SL.TERMINAL_ID AND SH.SHIFT_ID = SL.SHIFT_ID " "LEFT JOIN GARTICLES GA ON GA.GARTICLE_ID = A.GARTICLE_ID " "WHERE SL.AMOUNT > 0 " "ORDER BY A.GARTICLE_ID" ) .arg(m_terminalID) .arg(m_shiftID); // Выполняем запрос if(!q.exec(strSQL)) { qInfo(logInfo()) << "Errog goodlist" << q.lastError().text(); emit finish(); } // Цикл получения записей, и добавления их в вектор while (q.next()){ ar.setID(q.value("GARTICLE_ID").toInt()); ar.setShortName(q.value("SHORTNAME").toString().trimmed()); ar.setAmount(q.value("AMOUNT").toFloat()); ar.setPrice(q.value("PRICE").toFloat()); goods.append(ar); } //Передаем результат в основной поток emit signalSendArticlesList(goods); //Поток закончил работу emit finish(); }
Datenabruf-Thread starten
// Выбор данных происходит при переходе на определенную странцу QWizardPage void ArticlePage::initializePage() { // Создаем объект класса и передаем ему параметры ListArticles *lsArticles = new ListArticles(recrodConn, field("terminalID").toInt(),field("shiftID").toInt()); // Создаем поток в которм будут производиться наша выборка QThread *thread = new QThread(); // Перемещаем объект класса в поток lsArticles->moveToThread(thread); //// Сигналы и слоты для взаимидействия с потоком // при старте потока выполняем некоторые действия в текущем потоке. // В моем случае на просто засекаю начало выбоки данных connect(thread,&QThread::started,this,&ArticlePage::slotStartArticlesList); // При старте потока начинаем выборку данных connect(thread,&QThread::started,lsArticles,&ListArticles::createListGoods); // Передача результирующего объекта QVertor из дочернего потока в основной connect(lsArticles,&ListArticles::signalSendArticlesList,this,&ArticlePage::slotGetArticlesList,Qt::DirectConnection); // Окончание работы потока по завершению выбрки данных connect(lsArticles,&ListArticles::finish,thread,&QThread::quit); // Удаляем объект в потоке connect(lsArticles,&ListArticles::finish,lsArticles,&ListArticles::deleteLater); // Вы полняем действия по в основном потоке после завершения дочернего connect(lsArticles,&ListArticles::finish,this,&ArticlePage::slotFinishArticlesList); // Прощаемся с дочерним потоком connect(thread,&QThread::finished,thread,&QThread::deleteLater); // Запускаем поток thread->start(); }
Slots für den Datenaustausch mit dem Stream
void ArticlePage::slotGetArticlesList(QVector<Articles> ls) { //Получаем вектор с результами из потока goods = ls; } void ArticlePage::slotStartArticlesList() { qInfo(logInfo()) << "Начали получать список товаров" << QTime::currentTime().toString("hh:mm:ss.zzz"); } void ArticlePage::slotFinishArticlesList() { // на основании полученного вектора с данными создаем модель данных типа VectorModel и выводим ее в QTableView qInfo(logInfo()) << "Закончили получать список товаров" << QTime::currentTime().toString("hh:mm:ss.zzz"); ui->frameProgress->hide(); ui->groupBoxAdd->show(); modelArticles = new VektorModel(goods); ui->tableView->setModel(modelArticles); ui->tableView->verticalHeader()->hide(); ui->tableView->setAlternatingRowColors(true); ui->tableView->resizeColumnsToContents(); // Минимальная высота строк в QTableView ui->tableView->verticalHeader()->setDefaultSectionSize(ui->tableView->verticalHeader()->minimumSectionSize()); }
Vektormodell
Eine von QAbstractTableModel abgeleitete Klasse, die ein Modell zum Anzeigen von Abfragedaten erstellt.
Vektormodell.h
#ifndef VEKTORMODEL_H #define VEKTORMODEL_H #include "articles.h" #include <QObject> #include <QAbstractTableModel> #include <QVariant> class VektorModel : public QAbstractTableModel { Q_OBJECT QVector<Articles> ar; public: VektorModel(const QVector<Articles> vek); QVariant data(const QModelIndex &index, int role) const; QVariant headerData( int section, Qt::Orientation orientation, int role ) const; int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; }; #endif // VEKTORMODEL_H
Vektormodell.cpp
#include "vektormodel.h" #include "articles.h" VektorModel::VektorModel(const QVector<Articles> vek) { ar = vek; } // отображение данных модели QVariant VektorModel::data(const QModelIndex &index, int role) const { if ( !index.isValid() ) { return QVariant(); } Articles a = ar[index.row()]; switch (role) { case Qt::DisplayRole: switch (index.column()) { case 0: return a.getID(); // 1-й столбец ID case 1: return a.getShortName(); // 2-й столбец Наименование case 2: return a.getAmount(); // 3-й столбец Количетсво case 3: return QString::number(a.getPrice(),'f',2); // 4-й столбец Цена default: break; } break; // выравниваем 2-й и 3-й столбез по правому краю case Qt::TextAlignmentRole: if(index.column() == 2 || index.column() == 3 ) return Qt::AlignRight; break; default: break; } return QVariant(); } QVariant VektorModel::headerData(int section, Qt::Orientation orientation, int role) const { //Cоздаем заголовки столбцов модели if( role != Qt::DisplayRole ) { return QVariant(); } if( orientation == Qt::Vertical ) { return section; } switch( section ) { case 0: return tr( "Гл.Код" ); case 1: return tr( "Наименование" ); case 2: return tr( "Доступно" ); case 3: return tr( "Цена" ); } return QVariant(); } int VektorModel::rowCount(const QModelIndex &parent) const { // Количетсво строк return ar.size(); } int VektorModel::columnCount(const QModelIndex &parent) const { // Количество столбцов Articles a; return a.getColParam(); }
Als Ergebnis erhalten wir Folgendes:
В процессе отладки на реальных базах данных столкнулся с проблемой что перенос данных из объекта QSqlQuery в вектор выполняется достаточно медленно.
Проблема решилась замена названий в value на номера.
Добрый день, вывел в поток ресурсоемкую функцию, интерфейс работает не нарадуешься, но после завершения потока теряется соединение с базой данных в основоном, не могу сообразить куда смотреть. В потоке создается свое соединение с базой данных.
В потоке надо создавать свое соединение с БД с другим именем.
Если в:
QSQLDatabase db_thread = QSQLDatabase::addDatabase("MYSQL","db_new_name");
крашится после запуска сразу
Вы полностью создаете новое соединение?
И при создании объекта QSqlQuery или модели указываете алиас подключения?
Упс, видимо нет, буду проверять. У меня просто собраны функции работы с базой данных, и подключение новое, но в функция
Как пример
Перенес в класс потока все функции для работы с БД, но все по старому когда закрываю поток основное соединение тоже закрывается
Соединение в потоке используется только в потоке. Т.е выбирает данные сохраняете в какой-нибудь контейнер и передаете его основному потоку.
Я наверное слишком туп, можете пример привести как Вы это делаете?
Так эта статья как раз об этом.
Кстати сегодня почему-то все заработало :)
Луна стала в нужную фазу :-)