Список АЗС с которыми будем производить дальнейшие действия мы получили.
Создаем слот вызываемый при нажатии на 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);
- }
В результате получаем следующее
По завершению опроса всех АЗС отображается статус опроса.
Такой способ реализации взаимодействия потоков и основным потоком мне показался наглядным. Если у кого-то есть замечания и предложения по реализации с большой благодарностью выслушаю их.
Архив проекта: