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

QTableWidget, QThread

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

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Comments

Only authorized users can post comments.
Please, Log in or Sign up
Donate

Hello, Dear Users of EVILEG!!!

If the site helped you, then support the development of the site financially, please.

You can do it by following ways:

Thank you, Evgenii Legotckoi

n
Nov. 16, 2019, 1:28 a.m.
nick

C++ - Test 001. The first program and data types

  • Result:80points,
  • Rating points4
D
Nov. 15, 2019, 10:16 a.m.
Daulet

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:40points,
  • Rating points-8
LP
Nov. 12, 2019, 7:22 p.m.
Lev Parhimovich

C++ - Test 006. Enumerations

  • Result:50points,
  • Rating points-4
Last comments
b
Nov. 9, 2019, 7:28 p.m.
bastonc

спасибо ещё раз. огромное, за уделённое время
b
Nov. 9, 2019, 7:24 p.m.
bastonc

Спасибо Вам большое. Буду изучать.
Nov. 9, 2019, 4:58 p.m.
Evgenij Legotskoj

Добрый день. По первым двум вопросам вы найдёте ответ в этой статье - PyQt5 - Урок 008. Работа с QTableWidget (Обновление урока 006) Что касается последнего вопроса, то я вам…
Nov. 9, 2019, 1:50 p.m.
Evgenij Legotskoj

Как и обещал, вы можете посмотреть новую статью QML - Урок 037. Кастомизация кнопок в QML (Обновление урока 002) . Там же найдёте ссылку на Git репозиторий. Не забудьте поставить звёз…
b
Nov. 8, 2019, 6:40 p.m.
bastonc

Приветствую. Подскажите пожалуйста пару моментов. 1. Как сделать столбец не редактируемый, а остальные ячейки остаются редактируемыми 2. Как оталвливать события двойного клика для реда…
Now discuss on the forum
s
Nov. 15, 2019, 10:06 p.m.
sladkoewka

За пример буду очень благодарен, т.к. я новичок и с подобным пока не работал.
Nov. 15, 2019, 6:37 p.m.
Intruder

Евгений, почитав про эту проблему пришел к выводу, что либо нужно говорить очередь, либо все вернуть из библиотеки (dll в моем случае) в приложение, потому что в приложении все работает просто з…
Nov. 15, 2019, 5:06 p.m.
Evgenij Legotskoj

Ну тогда не знаю )) Я большую часть времени на C++ с Qt работаю, а PyQt5 у меня боком. Так что, чем можем помочь ))
H
Nov. 15, 2019, 4:18 p.m.
Hyperfish

Да, пробовал, с преобразованием int array[]={1,2,3,4,5,6,7} в jintArray(array). Если так, то программа компилируется без ошибок и предупреждений, но вываливается с ошибкой времени выполнени…
Nov. 15, 2019, 2:48 p.m.
Evgenij Legotskoj

Ну собственно поэтому я и сказал, что бесполезное это занятие.
EVILEG
About
Services
© EVILEG 2015-2019
Recommend hosting TIMEWEB