Data communication and data sharing between Qt threads

1. Background introduction

In the process of using QT for project development, multi-threading is often used, such as a thread for image acquisition, a thread for image processing, and a thread for data communication. There will be a need for data sharing in these different threads. There are three main ways to share data between Qt threads:

1. Use shared memory; that is, variables (global variables) that can be shared by both threads, so that both threads can access and modify the variables, thereby achieving the purpose;
2. Use the signal and slot mechanism to transfer data from one thread to another thread
3. Share class pointers to access variables and functions of different classes;

Benefit of this article,You can receive Qt development learning package and technical videos for free, including (Qt practical project video tutorial + code, C++ language basics, C++ design pattern , Introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project practice, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓ ↓Click at the bottom of the articleto receive the fee↓↓

2. Method introduction

The first method is to use global variables or global functions and call them in other classes or threads. This is a common method in various programming languages, but global variables occupy memory for a long time, affecting program space usage, and global variables are modified. It affects the entire program, and the security of the program cannot be guaranteed. Generally, global variables or functions should be used as little as possible. This method will not be introduced further.

2.1 Signal slot for data communication

The signal slot function is a unique function of QT. When using signal slots, you need to pay attention to the following matters:

Only the QObject class and its derived classes can use the signal and slot mechanism. When using signals and slots to communicate between threads, the slot parameters must use metadata type parameters; if a custom data type is used, it needs to be registered before connect. (qRegisterMetaType) is a metadata type; if signal slots are used to transfer parameters between threads, const must be added, because const literal constants exist in the constant area and have a life cycle as long as the program. This can avoid the invalid reference caused by the parameter’s runtime expiration when the slot is called;

Here I use a balser camera thread to collect images and send them to the demo displayed by the UI thread using signal slots to show the data communication between threads through signal slots.

/*Image acquisition thread header file*/
/*GrabThread.h*/
#pragma execution_character_set("utf-8")
#ifndef _GRABTHREAD_H
#define _GRABTHREAD_H
#include <Qtwidgets>
#include <QtCore>
#include <QtGui>
#include <pylon/PylonIncludes.h>
#include <QThread>
#include "opencv2/opencv.hpp"

using namespace Pylon;

class GrabThread : public QThread
{
    Q_OBJECT

public:
    GrabThread();
    ~GrabThread();

    void run();
    void init(CInstantCamera & amp;m_camera);
    bool isInit();
    void stop();
    void save(bool);
    void grab(int g =1);
    cv::Mat Result2Mat(CGrabResultPtr & amp;ptrGrabResult);

    CInstantCamera *m_camera;
    CGrabResultPtr ptrGrabResult; //Basler gets the result pointer
    CImageFormatConverter m_formatConverter;//Basler image format conversion class
    CPylonImage pylonImage; //Basler image format
    QImage m_image; //Qt image format
    QPixmap m_pix;
    String_t m_prefix;

    bool m_stop;
    bool m_init;
    bool m_save;
    
    int m_grab; // Image acquisition strategy 0 means continuous acquisition, 1 means single frame acquisition
    int m_num_one;
    int m_num_continue;
    
signals:
    //Signal sent to UI thread
    void ThreadPic(cv::Mat outputPix);

};
#endif// GRABTHREAD_H

GrabThread.cpp

#include "GrabThread.h"

GrabThread::GrabThread()
{
    m_formatConverter.OutputPixelFormat = PixelType_Mono8;
    m_stop = false;
    m_init = false;
    m_save = false;
    m_grab = 0;
    m_num_continue = 0;
    m_num_one = 0;
}

GrabThread::~GrabThread()
{
}

void GrabThread::run()
{
    try
    {
        m_camera->StartGrabbing(GrabStrategy_LatestImageOnly);
        while (m_camera->IsGrabbing() & amp; & amp; !m_stop)
        {
            m_camera->RetrieveResult(5000000, ptrGrabResult);
            if (ptrGrabResult->GrabSucceeded())
            {
                //Format conversion
                cv::Mat MatImg = Result2Mat(ptrGrabResult);
                // qDebug() << "Conversion successful" << endl;
                //transmit a signal
                emit ThreadPic(MatImg);
            }

        }
        m_stop = false;
        m_camera->StopGrabbing();
    }
    catch (const GenericException &e)
    {
        // Error handling.
        qDebug() << "An exception occurred." << endl
            << e.GetDescription() << endl;
    }
    return;
}

void GrabThread::init(CInstantCamera & amp;input_camera)
{
    m_camera = &input_camera;
    m_init = true;
}

bool GrabThread::isInit()
{
    return m_init;
}

void GrabThread::stop()
{
    m_stop = true;
    this->wait();
}

void GrabThread::save(bool s)
{
    m_save = s;
}

void GrabThread::grab(int g)
{
    m_grab = g;
}

cv::Mat GrabThread::Result2Mat(CGrabResultPtr & amp;ptrGrabResult)
{
    format conversion
    m_formatConverter.Convert(pylonImage, ptrGrabResult);
    uchar * din = (uchar *)(pylonImage.GetBuffer()); //data pointer
    cv::Mat cvImage = cv::Mat(ptrGrabResult->GetHeight(),ptrGrabResult->GetWidth(),CV_8UC1,din).clone();
    return cvImage;
}

The signal sent in the collection thread must have a corresponding slot function in the UI thread.

/* imgShowWidget.h */
#ifndef IMGSHOWWIDGET_H
#define IMGSHOWWIDGET_H

#include <QWidget>
#include "opencv2/opencv.hpp"

namespace Ui {
class ImgShowWidget;
}

class ImgShowWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ImgShowWidget(QWidget *parent = 0);
    ~ImgShowWidget();

private:
    Ui::ImgShowWidget *ui;
    QImage cvMat2QImage(const cv::Mat & amp; mat);
    cv::Mat QImage2Mat(QImage image);

private slots:
    //Slot function to display image
    void Thread_Img(cv::Mat img);
};
#endif // IMGSHOWWIDGET_H

ImgShowWidget.cpp

#include "imgshowwidget.h"
#include "ui_imgshowwidget.h"
#include <QDebug>
#include <QElapsedTimer>
using namespace cv;
ImgShowWidget::ImgShowWidget(QWidget *parent):
    QWidget(parent),
    ui(new Ui::ImgShowWidget)
{
    ui->setupUi(this);
    qRegisterMetaType<Mat>("Mat");
}

ImgShowWidget::~ImgShowWidget()
{
    delete ui;
}

void ImgShowWidget::Thread_Img(cv::Mat img)
{
    QImage Qimg;
    if(isWork)
    {
        QElapsedTimer ElapsedTimer;
        ElapsedTimer.start();
        Mat ResultImg = m_ProcessObj->DetectProcess(img);
        qDebug()<<"Time consuming"<<ElapsedTimer.elapsed()<<"Milliseconds";
        Qimg = cvMat2QImage(ResultImg);
    }
    else
    {
        Qimg = cvMat2QImage(img);
    }
    QPixmap m_pix = QPixmap::fromImage(Qimg);
    m_pix = m_pix.scaled(ui->PicShow->size(), Qt::KeepAspectRatio);
    ui->PicShow->setPixmap(m_pix);
}

QImage ImgShowWidget::cvMat2QImage(const cv::Mat & amp;mat)
{
    switch(mat.type())
    {
    // 8-bit 4 channel
    case CV_8UC4:
    {
        QImage image( (const uchar*)mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_RGB32);
        return image;
    }
        // 8-bit 3 channel
    case CV_8UC3:
    {
        QImage image( (const uchar*)mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_RGB888);
        return image.rgbSwapped();
    }
        // 8-bit 1 channel
    case CV_8UC1:
    {
        static QVector<QRgb> sColorTable;
        // only create our color table once
        if ( sColorTable.isEmpty() )
        {
            sColorTable.resize( 256 );
            for ( int i = 0; i < 256; + + i )
            {
                sColorTable[i] = qRgb( i, i, i );
            }
        }
        QImage image( (const uchar*)mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_Indexed8 );
        image.setColorTable( sColorTable );
        return image;
    }
    default:
        qDebug("Image format is not supported: depth=%d and %d channels\\
", mat.depth(), mat.channels());
        qWarning() << "cvMatToQImage - cv::Mat image type not handled in switch:" << mat.type();
        break;
    }

    return QImage();
}

Here is the first signal slot method. Send a signal through emit ThreadPic(MatImg). In the UI thread, use the slot function Thread_Img(cv::Mat img) to receive the Mat type image for display. The Mat type here is not Qt’s metadata. , so use qRegisterMetaType(“Mat”) to register.

2.2 Sharing class pointers to implement synchronous calls

If I create a data class to save the data during image processing, the collected images must be placed in the data class during image collection. The UI thread will also set different variable parameters and be placed in the data class. During image processing, When a thread wants to use data, it needs to go to the data class to read the data. With so many classes reading and writing at the same time, how can we achieve synchronous sharing? Here we need to share pointers after the UI thread creates each class.

 m_ImgProcessObj = new ImgProcessThread();
        //Initialize data class
        currentData = new MyData();
        m_Product = new productManager(this);
        m_Product->GetMyDataPoint(currentData);
        m_ImgProcessObj->GetMyDataPoint(currentData);

The GetMyDataPoint function is used here to share the data class pointer with the pointers of other classes that need to call the data. In fact, the two pointers point to the same memory address.

void ImgProcessThread::GetMyDataPoint(MyData *DPoint)
{
    DataPoint = DPoint;
}

In this way, the DataPoint pointer can be used in the image processing class to freely call the member variables and functions of the data class. Of course, the header file of the data class must be referenced here.

Benefit of this article,You can receive Qt development learning package and technical videos for free, including (Qt practical project video tutorial + code, C++ language basics, C++ design pattern , Introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project practice, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓ ↓Click at the bottom of the articleto receive the fee↓↓