Ruslan Polupan
Ruslan Polupan7 августа 2019 г. 10:41

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

Список АЗС с которыми будем производить дальнейшие действия мы получили.
Создаем слот вызываемый при нажатии на 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);
}

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

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

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

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

iMposCh010.zip iMposCh010.zip

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

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

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
d
  • dsfs
  • 26 апреля 2024 г. 11:56

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80баллов,
  • Очки рейтинга4
d
  • dsfs
  • 26 апреля 2024 г. 11:45

C++ - Тест 002. Константы

  • Результат:50баллов,
  • Очки рейтинга-4
d
  • dsfs
  • 26 апреля 2024 г. 11:35

C++ - Тест 001. Первая программа и типы данных

  • Результат:73баллов,
  • Очки рейтинга1
Последние комментарии
k
kmssr9 февраля 2024 г. 2:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко5 февраля 2024 г. 9:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 18:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 16:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 декабря 2023 г. 5:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
PS
Peter Son4 мая 2024 г. 0:57
Best Indian Food Restaurant In Cincinnati OH Ready to embark on a gastronomic journey like no other? Join us at App india restaurant and discover why we're renowned as the Best Indian Food Restaurant In Cincinnati OH . Whether y…
Evgenii Legotckoi
Evgenii Legotckoi2 мая 2024 г. 21:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderChe
IscanderChe30 апреля 2024 г. 11:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…
G
Gar22 апреля 2024 г. 12:46
Clipboard Как скопировать окно целиком в clipb?
Павел Дорофеев
Павел Дорофеев14 апреля 2024 г. 9:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь

Следите за нами в социальных сетях