Ruslan Polupan
7 августа 2019 г. 20:41

Проект iMpos. Часть 010. Получение наименований топлива

Список АЗС с которыми будем производить дальнейшие действия мы получили.
Создаем слот вызываемый при нажатии на CheckBox на на GroupBox Действия.

  1. void FuelNameDialog::on_groupBoxActions_clicked(bool checked)
  2. {
  3. if(checked){
  4. //Очищаем список терминалов передаваемых для дальнейшей обработки
  5. listTerminals.clear();
  6. //Добавляем отмеченные терминалы в список
  7. int rowCount = ui->tableWidgetTerm->rowCount();
  8. for(int i=0; i<rowCount; ++i){
  9. QWidget *item = ui->tableWidgetTerm->cellWidget(i,0);
  10. QCheckBox *checkBox = qobject_cast<QCheckBox*>(item->layout()->itemAt(0)->widget());
  11. if(checkBox->isChecked()){
  12. listTerminals.append(ui->tableWidgetTerm->item(i,1)->data(Qt::DisplayRole).toInt());
  13. }
  14. }
  15. //Проверяем что список не пустой
  16. if(listTerminals.size()==0){
  17. QMessageBox::warning(this, "Ошибка ввода","Нет выбранных терминалов");
  18. ui->groupBoxActions->setChecked(false);
  19. return;
  20. }
  21. }
  22. ui->groupBoxFuel->setEnabled(!checked);
  23. }

По умолчанию на вкладке Просмотр выбрана опция Показать отчёт.
Назначаем слоты на обработку сигналов от buttonBoxView:

  1. void FuelNameDialog::on_buttonBoxView_rejected()
  2. {
  3. ui->groupBoxFuel->setEnabled(true);
  4. ui->groupBoxActions->setChecked(false);
  5. }
  6.  
  7. void FuelNameDialog::on_buttonBoxView_accepted()
  8. {
  9. //Диалог для отображения результатов и прогресса получения данных с АЗС
  10. ViewFuelNameDialog *viewFnDlg = new ViewFuelNameDialog(&listTerminals,this);
  11. viewFnDlg->exec();
  12. }

Общий алгоритм получения данных о наименовании видов топлива следующий:
- получаем параметры подключения к базе данных азс;
- используя потоки получаем информацию о подключении с отображения текущего прогресса по каждой АЗС;
- предоставляем пользователю результат в виде таблицы.

Для хранения информации о наименовании создадим тип данных FuelName, который позволит хранить и получать информацию и содержит следующие поля:
- номер резервуара;
- код вида топлива;
- краткое наименование топлива;
- полное наименование топлива которое отображается в фискальном чеке.

Создаем класс FuelName. В заголовочный файл добавляем свойства:

  1. int m_tankID; // номер резервуара;
  2. int m_fuelID; // код вида топлива;
  3. QString m_shortName; // краткое наименование топлива;
  4. QString m_name; // полное наименование топлива которое отображается в фискальном чеке.

Qt Creator позволяет быстро создать методу установки получения значения свойства. Для этого вызываем контекстное меню на имени свойства, выбираем Рефакторинг Создать методы получения и установки значения. Повторяем действия для всех свойств нашего класса.

В результате получаем следующее:

fuelname.h

  1. #ifndef FUELNAME_H
  2. #define FUELNAME_H
  3. #include <QString>
  4.  
  5. class FuelName
  6. {
  7. public:
  8. FuelName();
  9. int tankID() const;
  10. void setTankID(int tankID);
  11.  
  12. int fuelID() const;
  13. void setFuelID(int fuelID);
  14.  
  15. QString shortName() const;
  16. void setShortName(const QString &shortName);
  17.  
  18. QString name() const;
  19. void setName(const QString &name);
  20.  
  21. private:
  22. int m_tankID; // номер резервуара;
  23. int m_fuelID; // код вида топлива;
  24. QString m_shortName; // краткое наименование топлива;
  25. QString m_name; // полное наименование топлива которое отображается в фискальном чеке.
  26. };
  27.  
  28. #endif // FUELNAME_H

fuelname.cpp

  1. #include "fuelname.h"
  2.  
  3. FuelName::FuelName()
  4. {
  5.  
  6. }
  7.  
  8. int FuelName::tankID() const
  9. {
  10. return m_tankID;
  11. }
  12.  
  13. void FuelName::setTankID(int tankID)
  14. {
  15. m_tankID = tankID;
  16. }
  17.  
  18. int FuelName::fuelID() const
  19. {
  20. return m_fuelID;
  21. }
  22.  
  23. void FuelName::setFuelID(int fuelID)
  24. {
  25. m_fuelID = fuelID;
  26. }
  27.  
  28. QString FuelName::shortName() const
  29. {
  30. return m_shortName;
  31. }
  32.  
  33. void FuelName::setShortName(const QString &shortName)
  34. {
  35. m_shortName = shortName;
  36. }
  37.  
  38. QString FuelName::name() const
  39. {
  40. return m_name;
  41. }
  42.  
  43. void FuelName::setName(const QString &name)
  44. {
  45. m_name = name;
  46. }

Для хранения информации о всех видов топлива на АЗС создадим еще один класс AzsFuelName:
azsfuelname.h

  1. #ifndef AZSFUELNAME_H
  2. #define AZSFUELNAME_H
  3. #include "fuelname.h"
  4. #include <QList>
  5. #include <QString>
  6.  
  7. class AzsFuelName
  8. {
  9. public:
  10. AzsFuelName();
  11. QList<FuelName> listFuels() const;
  12. void insertFuelName(int tankID, int fuelID, QString shortName, QString name); //Добавления наименования топлива
  13. int terminalID() const;
  14. void setTerminalID(int terminalID);
  15. private:
  16. int m_terminalID; //Номер терминала
  17. QList<FuelName> m_listFuels;//Список наименований топлива
  18. };
  19.  
  20. #endif // AZSFUELNAME_H

azsfuelname.cpp

  1. #include "azsfuelname.h"
  2.  
  3. AzsFuelName::AzsFuelName()
  4. {
  5.  
  6. }
  7.  
  8. QList<FuelName> AzsFuelName::listFuels() const
  9. {
  10. return m_listFuels;
  11. }
  12.  
  13. void AzsFuelName::insertFuelName(int tankID, int fuelID, QString shortName, QString name)
  14. {
  15. FuelName fn;
  16. fn.setTankID(tankID);
  17. fn.setFuelID(fuelID);
  18. fn.setShortName(shortName);
  19. fn.setName(name);
  20. m_listFuels.append(fn);
  21. }
  22.  
  23. int AzsFuelName::terminalID() const
  24. {
  25. return m_terminalID;
  26. }
  27.  
  28. void AzsFuelName::setTerminalID(int terminalID)
  29. {
  30. m_terminalID = terminalID;
  31. }

Создадим заголовочный файл в котором определим структуру для хранения текущего статуса выполнения запроса в потоке и перечисление текущих статусов выполнения запросов.
statusthread.h

  1. #ifndef STATUSTHREAD_H
  2. #define STATUSTHREAD_H
  3. #include <QObject>
  4.  
  5. struct statusThread //Структура для хранения текущего статуса
  6. {
  7. int terminalId; //Номер терминала
  8. int currentStatus; //Текущий статус операции получения наименования
  9. };
  10.  
  11. enum statusList { //Список статусов
  12. CONNECT_TO_DATABASE, //Соединение с базой данных
  13. SELECT_FUEL_NAME, //Выполнение запроса
  14. ERROR_OPEN_DATABASE, //Ошибка отрытия базы данных
  15. ERROR_GET_FUEL_NAME, //Ошибка выполнения запроса
  16. FINISHED //Удачное завершение запроса
  17. };
  18.  
  19. //Разрешаем использовать данный тип данных во взаимодействии сигнал-слот
  20. Q_DECLARE_METATYPE(statusThread);
  21.  
  22. #endif // STATUSTHREAD_H

Для реализации получения информации в потоке создадим класс в котором будет осуществляться получения списка наименований топлива GetFuelNameClass.

getfuelnameclass.h

  1. #ifndef GETFUELNAMECLASS_H
  2. #define GETFUELNAMECLASS_H
  3. #include "statusthread.h"
  4. #include "azsfuelname.h"
  5. #include <QObject>
  6. #include <QSqlQuery>
  7. #include <QSqlError>
  8.  
  9.  
  10. class GetFuelNameClass : public QObject
  11. {
  12. Q_OBJECT
  13. public:
  14. explicit GetFuelNameClass(QStringList connList, QObject *parent = nullptr);
  15.  
  16. signals:
  17. void finisList(); //Получение наименований завершено
  18. void signalSendStatus(statusThread); //Текущий статус получеия наименований
  19. void signalSendAzsFuelName(AzsFuelName); //Отправка наименований видов топлива в основной поток
  20. public slots:
  21. void getFuelList(); //Получение наименований топлива
  22. private:
  23. QStringList m_connList; //Параменты подключения к базе данный АЗС
  24. statusThread currentStatus; //Текущий статус выполнения
  25. };
  26.  
  27. #endif // GETFUELNAMECLASS_H

getfuelnameclass.cpp

  1. #include "getfuelnameclass.h"
  2. #include "LoggingCategories/loggingcategories.h"
  3.  
  4. GetFuelNameClass::GetFuelNameClass(QStringList connList, QObject *parent) :
  5. QObject(parent),
  6. m_connList(connList)
  7. {
  8. typedef statusThread st;
  9. qRegisterMetaType<st>("st");
  10. }
  11.  
  12. void GetFuelNameClass::getFuelList()
  13. {
  14. //Устанавливаем текущий статус выполнения
  15. currentStatus.terminalId=m_connList[0].toInt();
  16. currentStatus.currentStatus=CONNECT_TO_DATABASE;
  17. emit signalSendStatus(currentStatus);
  18.  
  19. //Cоздаем подключение к базе данных АЗС
  20. QSqlDatabase db = QSqlDatabase::addDatabase("QIBASE", m_connList[0]);
  21.  
  22. db.setHostName(m_connList[1]);
  23. db.setDatabaseName(m_connList[2]);
  24. db.setUserName("SYSDBA");
  25. db.setPassword(m_connList[3]);
  26.  
  27. //Подключаемся к базе данных АЗС
  28. if(!db.open()){
  29. //Не удалось подключится
  30. qCritical(logCritical()) << Q_FUNC_INFO << "Невозможно подключится к базе данных АСЗ" << m_connList[0] << db.lastError().text();
  31. //Меняем статус выполнения
  32. currentStatus.currentStatus=ERROR_OPEN_DATABASE;
  33. //Отправляем статус главному потоку
  34. emit signalSendStatus(currentStatus);
  35. //Завершаем выполнение по данной АЗС
  36. emit finisGetList();
  37. return;
  38. }
  39. //Меняем статус выполнения и отправляем его в главный поток
  40. currentStatus.currentStatus=SELECT_FUEL_NAME;
  41. emit signalSendStatus(currentStatus);
  42.  
  43. //Создаем и подготавливаем запрос
  44. QSqlQuery q = QSqlQuery(db);
  45. q.prepare("select t.TANK_ID, f.FUEL_ID, f.SHORTNAME, f.NAME from FUELS f "
  46. "LEFT JOIN tanks t ON t.FUEL_ID = f.FUEL_ID "
  47. "where f.ISACTIVE='T' "
  48. "order by t.TANK_ID");
  49. //выполняем запрос
  50. if(!q.exec()) {
  51. //Запрос выполнить не удалось
  52. qCritical(logCritical()) << Q_FUNC_INFO << "Не возможно получить список видов топлива с АЗС." << m_connList[0] << q.lastError().text();
  53. //Меняем статус выполнения и отправляем его в главный поток
  54. currentStatus.currentStatus=ERROR_GET_FUEL_NAME;
  55. emit signalSendStatus(currentStatus);
  56. emit finisGetList();
  57. return;
  58. }
  59. AzsFuelName _fuelName;
  60. _fuelName.setTerminalID(m_connList[0].toInt());
  61. while(q.next()){
  62. _fuelName.insertFuelName(q.value(0).toInt(),q.value(1).toInt(),q.value(2).toString(),q.value(3).toString());
  63. }
  64. currentStatus.currentStatus=FINISHED;
  65. emit signalSendAzsFuelName(_fuelName);
  66. emit signalSendStatus(currentStatus);
  67. emit finisGetList();
  68. }
  69.  

Реализация диалога отображения процесса получения наименований видов топлива
viewfuelnamedialog.h

  1. #ifndef VIEWFUELNAMEDIALOG_H
  2. #define VIEWFUELNAMEDIALOG_H
  3. #include "statusthread.h"
  4. #include "azsfuelname.h"
  5. #include <QDialog>
  6. #include <QSqlQuery>
  7. #include <QSqlError>
  8.  
  9. namespace Ui {
  10. class ViewFuelNameDialog;
  11. }
  12.  
  13. class ViewFuelNameDialog : public QDialog
  14. {
  15. Q_OBJECT
  16.  
  17. public:
  18. explicit ViewFuelNameDialog(QList<int> *listTerm, QWidget *parent = nullptr);
  19. ~ViewFuelNameDialog();
  20.  
  21. private slots:
  22.  
  23. public slots:
  24. void slotGetStatusThread(statusThread status); //Обработка статуса выполнения запроса
  25. void slotGetAzsFuelName(AzsFuelName azsFuelname); //Получение списка наименований по терминалу
  26. private:
  27. void createUI();
  28. void getConnectionsList(); //Получения дагнных о подключении к базам данных АЗС
  29. void fuelNameList(); //Наименования видов топлива
  30.  
  31. private:
  32. Ui::ViewFuelNameDialog *ui;
  33. QList<int> *m_terminalSList; //Список терминалов с которыми будем работать
  34. QList<QStringList> m_connectionsList; //Cписок данных для подключения к базе данных АЗС
  35. QList<AzsFuelName> m_listFuelName; //Список наименований топлива
  36. QStringList statusText; //Список описанияй статуса подключений
  37. };
  38.  
  39. #endif // VIEWFUELNAMEDIALOG_H

viewfuelnamedialog.cpp

  1. #include "viewfuelnamedialog.h"
  2. #include "ui_viewfuelnamedialog.h"
  3. #include "passconv.h"
  4. #include "getfuelnameclass.h"
  5. #include "LoggingCategories/loggingcategories.h"
  6. #include <QThread>
  7.  
  8. ViewFuelNameDialog::ViewFuelNameDialog(QList<int> *listTerm, QWidget *parent) :
  9. QDialog(parent),
  10. ui(new Ui::ViewFuelNameDialog),
  11. m_terminalSList(listTerm)
  12. {
  13. ui->setupUi(this);
  14.  
  15. //Описания статосов выполнения запроса
  16. statusText.insert(CONNECT_TO_DATABASE,"Подключение к базе данных АЗС...");
  17. statusText.insert(SELECT_FUEL_NAME,"Получение списка видов топлива....");
  18. statusText.insert(ERROR_OPEN_DATABASE,"Ошибка открытия базы данных АЗС!");
  19. statusText.insert(ERROR_GET_FUEL_NAME, "Ошибка получения списка наименований топлива!");
  20. statusText.insert(FINISHED,"Готово!");
  21.  
  22. createUI();
  23. getConnectionsList();
  24. fuelNameList();
  25. }
  26.  
  27. ViewFuelNameDialog::~ViewFuelNameDialog()
  28. {
  29. delete ui;
  30. }
  31.  
  32. void ViewFuelNameDialog::createUI()
  33. {
  34. ui->tableWidget->setColumnCount(2);
  35. ui->tableWidget->verticalHeader()->hide();
  36. ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "АЗС" << "Статус");
  37. ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
  38. ui->tableWidget->verticalHeader()->setDefaultSectionSize(36);
  39. }
  40. //Получение параметров подключения к базам данных азс
  41. void ViewFuelNameDialog::getConnectionsList()
  42. {
  43. QSqlQuery q;
  44. QString strIN="";
  45. for(int i=0;i<m_terminalSList->size();++i){
  46. strIN += QString::number(m_terminalSList->at(i))+",";
  47. }
  48. strIN.resize(strIN.size()-1);
  49. QString strSQL = QString("select c.TERMINAL_ID, c.SERVER_NAME, c.DB_NAME, c.CON_PASSWORD from CONNECTIONS c "
  50. "where c.TERMINAL_ID IN(%1) and c.CONNECT_ID=2").arg(strIN);
  51. QStringList list;
  52. if(!q.exec(strSQL)) qCritical(logCritical()) << "Не удалось получить список терминалов" << q.lastError().text();
  53. while(q.next()){
  54. list.clear();
  55. list << q.value(0).toString() << q.value(1).toString() << q.value(2).toString() << passConv(q.value(3).toString());
  56. m_connectionsList.append(list);
  57. }
  58. }
  59.  
  60. void ViewFuelNameDialog::fuelNameList()
  61. {
  62. //Количетсво обрабатываемх АЗС
  63. int _azsCount = m_connectionsList.size();
  64. ui->progressBarGetFuel->setRange(0, _azsCount);
  65. ui->progressBarGetFuel->setValue(0);
  66. ui->progressBarGetFuel->setFormat("Обработано %v из %m");
  67.  
  68.  
  69. for(int i=0; i<_azsCount; i++){
  70. //Создаем объект класса получения наиметований и потока
  71. GetFuelNameClass *getFuel = new GetFuelNameClass(m_connectionsList.at(i));
  72. QThread *thread = new QThread();
  73. //помещаем класс в поток.
  74. getFuel->moveToThread(thread);
  75. //Связываем сигналы и слоты
  76. connect(thread,&QThread::started, getFuel, &GetFuelNameClass::getFuelList);
  77. connect(getFuel,&GetFuelNameClass::signalSendStatus,this,&ViewFuelNameDialog::slotGetStatusThread,Qt::UniqueConnection);
  78. connect(getFuel,&GetFuelNameClass::signalSendAzsFuelName,this,&ViewFuelNameDialog::slotGetAzsFuelName,Qt::DirectConnection);
  79. connect(getFuel, &GetFuelNameClass::finisGetList, getFuel, &GetFuelNameClass::deleteLater);
  80. connect(getFuel, &GetFuelNameClass::finisGetList, thread, &QThread::quit);
  81. connect(thread, &QThread::finished, thread, &QThread::deleteLater);
  82. thread->start();
  83. }
  84. }
  85.  
  86. void ViewFuelNameDialog::slotGetStatusThread(statusThread status)
  87. {
  88. if(status.currentStatus == CONNECT_TO_DATABASE){
  89. //Статус соединения с базой
  90. //Добавляем строки в TableWidget
  91. int row = ui->tableWidget->rowCount();
  92. ui->tableWidget->insertRow(row);
  93. ui->tableWidget->setItem(row,0, new QTableWidgetItem(QString::number(status.terminalId)));
  94. ui->tableWidget->setItem(row,1, new QTableWidgetItem(statusText[status.currentStatus]));
  95. ui->tableWidget->item(row,0)->setIcon(QIcon(":/Image/azs.png"));
  96. ui->tableWidget->item(row,1)->setIcon(QIcon(":/Image/database.png"));
  97. ui->tableWidget->item(row,1)->setBackground(QBrush("#F4FA58"));
  98. ui->tableWidget->sortByColumn(0,Qt::AscendingOrder);
  99. return;
  100. }
  101.  
  102. int rowCount = ui->tableWidget->rowCount();
  103. for (int i=0;i<rowCount;++i) {
  104. //Находим строку в TableWidget и изменяем ее в соответсвии со статусом
  105. if(ui->tableWidget->item(i,0)->text().toInt() == status.terminalId){
  106. ui->tableWidget->item(i,1)->setText(statusText[status.currentStatus]);
  107. switch (status.currentStatus) {
  108. case SELECT_FUEL_NAME:
  109. ui->tableWidget->item(i,1)->setBackground(QBrush("#D7DF01"));
  110. ui->tableWidget->item(i,1)->setIcon(QIcon(":/Image/selectfuel.png"));
  111. break;
  112. case ERROR_OPEN_DATABASE:
  113. ui->tableWidget->item(i,1)->setBackground(QBrush("#FE2E2E"));
  114. ui->tableWidget->item(i,1)->setIcon(QIcon(":/Image/error.png"));
  115. ui->progressBarGetFuel->setValue(ui->progressBarGetFuel->value()+1);
  116. break;
  117. case ERROR_GET_FUEL_NAME:
  118. ui->tableWidget->item(i,1)->setBackground(QBrush("#DF01A5"));
  119. ui->tableWidget->item(i,1)->setIcon(QIcon(":/Image/error.png"));
  120. ui->progressBarGetFuel->setValue(ui->progressBarGetFuel->value()+1);
  121. break;
  122. case FINISHED:
  123. ui->tableWidget->item(i,1)->setBackground(QBrush("#BFFF00"));
  124. ui->tableWidget->item(i,1)->setIcon(QIcon(":/Image/Accept.png"));
  125. ui->progressBarGetFuel->setValue(ui->progressBarGetFuel->value()+1);
  126. break;
  127. default:
  128. break;
  129. }
  130. break;
  131. }
  132. }
  133. }
  134.  
  135. void ViewFuelNameDialog::slotGetAzsFuelName(AzsFuelName azsFuelname)
  136. {
  137. //Добавляем полученный список наименований
  138. m_listFuelName.append(azsFuelname);
  139. }

В результате получаем следующее

По завершению опроса всех АЗС отображается статус опроса.

Такой способ реализации взаимодействия потоков и основным потоком мне показался наглядным. Если у кого-то есть замечания и предложения по реализации с большой благодарностью выслушаю их.

Архив проекта:

iMposCh010.zip iMposCh010.zip

По статье задано0вопрос(ов)

2

Вам это нравится? Поделитесь в социальных сетях!

Комментарии

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