Список АЗС с которыми будем производить дальнейшие действия мы получили.
Создаем слот вызываемый при нажатии на CheckBox на на GroupBox Действия.
void FuelNameDialog::on_groupBoxActions_clicked(bool checked) { if(checked){ //Очищаем список терминалов передаваемых для дальнейшей обработки listTerminals.clear(); //Добавляем отмеченные терминалы в список int rowCount = ui->tableWidgetTerm->rowCount(); for(int i=0; i<rowCount; ++i){ QWidget *item = ui->tableWidgetTerm->cellWidget(i,0); QCheckBox *checkBox = qobject_cast<QCheckBox*>(item->layout()->itemAt(0)->widget()); if(checkBox->isChecked()){ listTerminals.append(ui->tableWidgetTerm->item(i,1)->data(Qt::DisplayRole).toInt()); } } //Проверяем что список не пустой if(listTerminals.size()==0){ QMessageBox::warning(this, "Ошибка ввода","Нет выбранных терминалов"); ui->groupBoxActions->setChecked(false); return; } } ui->groupBoxFuel->setEnabled(!checked); }
По умолчанию на вкладке Просмотр выбрана опция Показать отчёт.
Назначаем слоты на обработку сигналов от buttonBoxView:
void FuelNameDialog::on_buttonBoxView_rejected() { ui->groupBoxFuel->setEnabled(true); ui->groupBoxActions->setChecked(false); } void FuelNameDialog::on_buttonBoxView_accepted() { //Диалог для отображения результатов и прогресса получения данных с АЗС ViewFuelNameDialog *viewFnDlg = new ViewFuelNameDialog(&listTerminals,this); viewFnDlg->exec(); }
Общий алгоритм получения данных о наименовании видов топлива следующий:
- получаем параметры подключения к базе данных азс;
- используя потоки получаем информацию о подключении с отображения текущего прогресса по каждой АЗС;
- предоставляем пользователю результат в виде таблицы.
Для хранения информации о наименовании создадим тип данных FuelName, который позволит хранить и получать информацию и содержит следующие поля:
- номер резервуара;
- код вида топлива;
- краткое наименование топлива;
- полное наименование топлива которое отображается в фискальном чеке.
Создаем класс FuelName. В заголовочный файл добавляем свойства:
int m_tankID; // номер резервуара; int m_fuelID; // код вида топлива; QString m_shortName; // краткое наименование топлива; QString m_name; // полное наименование топлива которое отображается в фискальном чеке.
Qt Creator позволяет быстро создать методу установки получения значения свойства. Для этого вызываем контекстное меню на имени свойства, выбираем Рефакторинг Создать методы получения и установки значения. Повторяем действия для всех свойств нашего класса.
В результате получаем следующее:
fuelname.h
#ifndef FUELNAME_H #define FUELNAME_H #include <QString> class FuelName { public: FuelName(); int tankID() const; void setTankID(int tankID); int fuelID() const; void setFuelID(int fuelID); QString shortName() const; void setShortName(const QString &shortName); QString name() const; void setName(const QString &name); private: int m_tankID; // номер резервуара; int m_fuelID; // код вида топлива; QString m_shortName; // краткое наименование топлива; QString m_name; // полное наименование топлива которое отображается в фискальном чеке. }; #endif // FUELNAME_H
fuelname.cpp
#include "fuelname.h" FuelName::FuelName() { } int FuelName::tankID() const { return m_tankID; } void FuelName::setTankID(int tankID) { m_tankID = tankID; } int FuelName::fuelID() const { return m_fuelID; } void FuelName::setFuelID(int fuelID) { m_fuelID = fuelID; } QString FuelName::shortName() const { return m_shortName; } void FuelName::setShortName(const QString &shortName) { m_shortName = shortName; } QString FuelName::name() const { return m_name; } void FuelName::setName(const QString &name) { m_name = name; }
Для хранения информации о всех видов топлива на АЗС создадим еще один класс AzsFuelName:
azsfuelname.h
#ifndef AZSFUELNAME_H #define AZSFUELNAME_H #include "fuelname.h" #include <QList> #include <QString> class AzsFuelName { public: AzsFuelName(); QList<FuelName> listFuels() const; void insertFuelName(int tankID, int fuelID, QString shortName, QString name); //Добавления наименования топлива int terminalID() const; void setTerminalID(int terminalID); private: int m_terminalID; //Номер терминала QList<FuelName> m_listFuels;//Список наименований топлива }; #endif // AZSFUELNAME_H
azsfuelname.cpp
#include "azsfuelname.h" AzsFuelName::AzsFuelName() { } QList<FuelName> AzsFuelName::listFuels() const { return m_listFuels; } void AzsFuelName::insertFuelName(int tankID, int fuelID, QString shortName, QString name) { FuelName fn; fn.setTankID(tankID); fn.setFuelID(fuelID); fn.setShortName(shortName); fn.setName(name); m_listFuels.append(fn); } int AzsFuelName::terminalID() const { return m_terminalID; } void AzsFuelName::setTerminalID(int terminalID) { m_terminalID = terminalID; }
Создадим заголовочный файл в котором определим структуру для хранения текущего статуса выполнения запроса в потоке и перечисление текущих статусов выполнения запросов.
statusthread.h
#ifndef STATUSTHREAD_H #define STATUSTHREAD_H #include <QObject> struct statusThread //Структура для хранения текущего статуса { int terminalId; //Номер терминала int currentStatus; //Текущий статус операции получения наименования }; enum statusList { //Список статусов CONNECT_TO_DATABASE, //Соединение с базой данных SELECT_FUEL_NAME, //Выполнение запроса ERROR_OPEN_DATABASE, //Ошибка отрытия базы данных ERROR_GET_FUEL_NAME, //Ошибка выполнения запроса FINISHED //Удачное завершение запроса }; //Разрешаем использовать данный тип данных во взаимодействии сигнал-слот Q_DECLARE_METATYPE(statusThread); #endif // STATUSTHREAD_H
Для реализации получения информации в потоке создадим класс в котором будет осуществляться получения списка наименований топлива GetFuelNameClass.
getfuelnameclass.h
#ifndef GETFUELNAMECLASS_H #define GETFUELNAMECLASS_H #include "statusthread.h" #include "azsfuelname.h" #include <QObject> #include <QSqlQuery> #include <QSqlError> class GetFuelNameClass : public QObject { Q_OBJECT public: explicit GetFuelNameClass(QStringList connList, QObject *parent = nullptr); signals: void finisList(); //Получение наименований завершено void signalSendStatus(statusThread); //Текущий статус получеия наименований void signalSendAzsFuelName(AzsFuelName); //Отправка наименований видов топлива в основной поток public slots: void getFuelList(); //Получение наименований топлива private: QStringList m_connList; //Параменты подключения к базе данный АЗС statusThread currentStatus; //Текущий статус выполнения }; #endif // GETFUELNAMECLASS_H
getfuelnameclass.cpp
#include "getfuelnameclass.h" #include "LoggingCategories/loggingcategories.h" GetFuelNameClass::GetFuelNameClass(QStringList connList, QObject *parent) : QObject(parent), m_connList(connList) { typedef statusThread st; qRegisterMetaType<st>("st"); } void GetFuelNameClass::getFuelList() { //Устанавливаем текущий статус выполнения currentStatus.terminalId=m_connList[0].toInt(); currentStatus.currentStatus=CONNECT_TO_DATABASE; emit signalSendStatus(currentStatus); //Cоздаем подключение к базе данных АЗС QSqlDatabase db = QSqlDatabase::addDatabase("QIBASE", m_connList[0]); db.setHostName(m_connList[1]); db.setDatabaseName(m_connList[2]); db.setUserName("SYSDBA"); db.setPassword(m_connList[3]); //Подключаемся к базе данных АЗС if(!db.open()){ //Не удалось подключится qCritical(logCritical()) << Q_FUNC_INFO << "Невозможно подключится к базе данных АСЗ" << m_connList[0] << db.lastError().text(); //Меняем статус выполнения currentStatus.currentStatus=ERROR_OPEN_DATABASE; //Отправляем статус главному потоку emit signalSendStatus(currentStatus); //Завершаем выполнение по данной АЗС emit finisGetList(); return; } //Меняем статус выполнения и отправляем его в главный поток currentStatus.currentStatus=SELECT_FUEL_NAME; emit signalSendStatus(currentStatus); //Создаем и подготавливаем запрос QSqlQuery q = QSqlQuery(db); q.prepare("select t.TANK_ID, f.FUEL_ID, f.SHORTNAME, f.NAME from FUELS f " "LEFT JOIN tanks t ON t.FUEL_ID = f.FUEL_ID " "where f.ISACTIVE='T' " "order by t.TANK_ID"); //выполняем запрос if(!q.exec()) { //Запрос выполнить не удалось qCritical(logCritical()) << Q_FUNC_INFO << "Не возможно получить список видов топлива с АЗС." << m_connList[0] << q.lastError().text(); //Меняем статус выполнения и отправляем его в главный поток currentStatus.currentStatus=ERROR_GET_FUEL_NAME; emit signalSendStatus(currentStatus); emit finisGetList(); return; } AzsFuelName _fuelName; _fuelName.setTerminalID(m_connList[0].toInt()); while(q.next()){ _fuelName.insertFuelName(q.value(0).toInt(),q.value(1).toInt(),q.value(2).toString(),q.value(3).toString()); } currentStatus.currentStatus=FINISHED; emit signalSendAzsFuelName(_fuelName); emit signalSendStatus(currentStatus); emit finisGetList(); }
Реализация диалога отображения процесса получения наименований видов топлива
viewfuelnamedialog.h
#ifndef VIEWFUELNAMEDIALOG_H #define VIEWFUELNAMEDIALOG_H #include "statusthread.h" #include "azsfuelname.h" #include <QDialog> #include <QSqlQuery> #include <QSqlError> namespace Ui { class ViewFuelNameDialog; } class ViewFuelNameDialog : public QDialog { Q_OBJECT public: explicit ViewFuelNameDialog(QList<int> *listTerm, QWidget *parent = nullptr); ~ViewFuelNameDialog(); private slots: public slots: void slotGetStatusThread(statusThread status); //Обработка статуса выполнения запроса void slotGetAzsFuelName(AzsFuelName azsFuelname); //Получение списка наименований по терминалу private: void createUI(); void getConnectionsList(); //Получения дагнных о подключении к базам данных АЗС void fuelNameList(); //Наименования видов топлива private: Ui::ViewFuelNameDialog *ui; QList<int> *m_terminalSList; //Список терминалов с которыми будем работать QList<QStringList> m_connectionsList; //Cписок данных для подключения к базе данных АЗС QList<AzsFuelName> m_listFuelName; //Список наименований топлива QStringList statusText; //Список описанияй статуса подключений }; #endif // VIEWFUELNAMEDIALOG_H
viewfuelnamedialog.cpp
#include "viewfuelnamedialog.h" #include "ui_viewfuelnamedialog.h" #include "passconv.h" #include "getfuelnameclass.h" #include "LoggingCategories/loggingcategories.h" #include <QThread> ViewFuelNameDialog::ViewFuelNameDialog(QList<int> *listTerm, QWidget *parent) : QDialog(parent), ui(new Ui::ViewFuelNameDialog), m_terminalSList(listTerm) { ui->setupUi(this); //Описания статосов выполнения запроса statusText.insert(CONNECT_TO_DATABASE,"Подключение к базе данных АЗС..."); statusText.insert(SELECT_FUEL_NAME,"Получение списка видов топлива...."); statusText.insert(ERROR_OPEN_DATABASE,"Ошибка открытия базы данных АЗС!"); statusText.insert(ERROR_GET_FUEL_NAME, "Ошибка получения списка наименований топлива!"); statusText.insert(FINISHED,"Готово!"); createUI(); getConnectionsList(); fuelNameList(); } ViewFuelNameDialog::~ViewFuelNameDialog() { delete ui; } void ViewFuelNameDialog::createUI() { ui->tableWidget->setColumnCount(2); ui->tableWidget->verticalHeader()->hide(); ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "АЗС" << "Статус"); ui->tableWidget->horizontalHeader()->setStretchLastSection(true); ui->tableWidget->verticalHeader()->setDefaultSectionSize(36); } //Получение параметров подключения к базам данных азс void ViewFuelNameDialog::getConnectionsList() { QSqlQuery q; QString strIN=""; for(int i=0;i<m_terminalSList->size();++i){ strIN += QString::number(m_terminalSList->at(i))+","; } strIN.resize(strIN.size()-1); QString strSQL = QString("select c.TERMINAL_ID, c.SERVER_NAME, c.DB_NAME, c.CON_PASSWORD from CONNECTIONS c " "where c.TERMINAL_ID IN(%1) and c.CONNECT_ID=2").arg(strIN); QStringList list; if(!q.exec(strSQL)) qCritical(logCritical()) << "Не удалось получить список терминалов" << q.lastError().text(); while(q.next()){ list.clear(); list << q.value(0).toString() << q.value(1).toString() << q.value(2).toString() << passConv(q.value(3).toString()); m_connectionsList.append(list); } } void ViewFuelNameDialog::fuelNameList() { //Количетсво обрабатываемх АЗС int _azsCount = m_connectionsList.size(); ui->progressBarGetFuel->setRange(0, _azsCount); ui->progressBarGetFuel->setValue(0); ui->progressBarGetFuel->setFormat("Обработано %v из %m"); for(int i=0; i<_azsCount; i++){ //Создаем объект класса получения наиметований и потока GetFuelNameClass *getFuel = new GetFuelNameClass(m_connectionsList.at(i)); QThread *thread = new QThread(); //помещаем класс в поток. getFuel->moveToThread(thread); //Связываем сигналы и слоты connect(thread,&QThread::started, getFuel, &GetFuelNameClass::getFuelList); connect(getFuel,&GetFuelNameClass::signalSendStatus,this,&ViewFuelNameDialog::slotGetStatusThread,Qt::UniqueConnection); connect(getFuel,&GetFuelNameClass::signalSendAzsFuelName,this,&ViewFuelNameDialog::slotGetAzsFuelName,Qt::DirectConnection); connect(getFuel, &GetFuelNameClass::finisGetList, getFuel, &GetFuelNameClass::deleteLater); connect(getFuel, &GetFuelNameClass::finisGetList, thread, &QThread::quit); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } } void ViewFuelNameDialog::slotGetStatusThread(statusThread status) { if(status.currentStatus == CONNECT_TO_DATABASE){ //Статус соединения с базой //Добавляем строки в TableWidget int row = ui->tableWidget->rowCount(); ui->tableWidget->insertRow(row); ui->tableWidget->setItem(row,0, new QTableWidgetItem(QString::number(status.terminalId))); ui->tableWidget->setItem(row,1, new QTableWidgetItem(statusText[status.currentStatus])); ui->tableWidget->item(row,0)->setIcon(QIcon(":/Image/azs.png")); ui->tableWidget->item(row,1)->setIcon(QIcon(":/Image/database.png")); ui->tableWidget->item(row,1)->setBackground(QBrush("#F4FA58")); ui->tableWidget->sortByColumn(0,Qt::AscendingOrder); return; } int rowCount = ui->tableWidget->rowCount(); for (int i=0;i<rowCount;++i) { //Находим строку в TableWidget и изменяем ее в соответсвии со статусом if(ui->tableWidget->item(i,0)->text().toInt() == status.terminalId){ ui->tableWidget->item(i,1)->setText(statusText[status.currentStatus]); switch (status.currentStatus) { case SELECT_FUEL_NAME: ui->tableWidget->item(i,1)->setBackground(QBrush("#D7DF01")); ui->tableWidget->item(i,1)->setIcon(QIcon(":/Image/selectfuel.png")); break; case ERROR_OPEN_DATABASE: ui->tableWidget->item(i,1)->setBackground(QBrush("#FE2E2E")); ui->tableWidget->item(i,1)->setIcon(QIcon(":/Image/error.png")); ui->progressBarGetFuel->setValue(ui->progressBarGetFuel->value()+1); break; case ERROR_GET_FUEL_NAME: ui->tableWidget->item(i,1)->setBackground(QBrush("#DF01A5")); ui->tableWidget->item(i,1)->setIcon(QIcon(":/Image/error.png")); ui->progressBarGetFuel->setValue(ui->progressBarGetFuel->value()+1); break; case FINISHED: ui->tableWidget->item(i,1)->setBackground(QBrush("#BFFF00")); ui->tableWidget->item(i,1)->setIcon(QIcon(":/Image/Accept.png")); ui->progressBarGetFuel->setValue(ui->progressBarGetFuel->value()+1); break; default: break; } break; } } } void ViewFuelNameDialog::slotGetAzsFuelName(AzsFuelName azsFuelname) { //Добавляем полученный список наименований m_listFuelName.append(azsFuelname); }
В результате получаем следующее
По завершению опроса всех АЗС отображается статус опроса.
Такой способ реализации взаимодействия потоков и основным потоком мне показался наглядным. Если у кого-то есть замечания и предложения по реализации с большой благодарностью выслушаю их.
Архив проекта: