赞
踩
在使用QT做项目开发过程中,经常会用到多线程,比如图像采集一个线程,图像处理一个线程、数据通讯一个线程。这些不同的线程中会出现数据共享的需求,Qt线程间共享数据主要有三种方式:
1.使用共享内存;即两个线程都能够共享的变量(全局变量),这样两个线程都能够访问和修改变量,从而达到恭喜目的;
2.使用信号槽机制,将数据从一个线程传递到另外一个线程
3.共享类指针来进行访问不同类的变量和函数;
第三种是我自己常用的方法,在下面我总结记录一下。
第一种方法,使用全局变量或全局函数,在其他类或线程中调用,这是各种编程语言中都通用的方法,但全局变量长时间占用内存,影响程序空间使用率,且全局变量修改影响整个程序,程序的安全性无法保证,一般尽量少用全局变量或函数,这种方法不展开介绍了。
信号槽功能是QT特有的功能,使用信号槽需要注意以下几个事项:
- 只有QObject类及其派生的类才能使用信号和槽的机制
- 在线程间使用信号槽进行通信时,槽参数必须使用元数据类型的参数;
- 如果使用自定义的数据类型,需要在connect之前将其注册(qRegisterMetaType)为元数据类型;
- 线程间用信号槽传递参数的话,要加const,因为const文字常量存在常量区中,生命周期和程序一样长。这样可以避免slot调用的时候参数的运行期已过造成引用无效;
这里我用一个balser相机线程采图使用信号槽发到UI线程显示的Demo来展示一下线程间通过信号槽的数据通讯。
/*图像采集线程头文件*/ /*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 &m_camera); bool isInit(); void stop(); void save(bool); void grab(int g =1); cv::Mat Result2Mat(CGrabResultPtr &ptrGrabResult); CInstantCamera *m_camera; CGrabResultPtr ptrGrabResult; //Basler 获取结果指针 CImageFormatConverter m_formatConverter;//Basler 图片格式转换类 CPylonImage pylonImage; //Basler 图像格式 QImage m_image; //Qt图片格式 QPixmap m_pix; String_t m_prefix; bool m_stop; bool m_init; bool m_save; int m_grab; //获取图像策略 0表示连续获取,1表示获取单帧 int m_num_one; int m_num_continue; signals: //发给UI线程的信号 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() && !m_stop) { m_camera->RetrieveResult(5000000, ptrGrabResult); if (ptrGrabResult->GrabSucceeded()) { //格式转换 cv::Mat MatImg = Result2Mat(ptrGrabResult); // qDebug() << "转换成功" << endl; //发射信号 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 &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 &ptrGrabResult) { 格式转换 m_formatConverter.Convert(pylonImage, ptrGrabResult); uchar * din = (uchar *)(pylonImage.GetBuffer()); //数据指针 cv::Mat cvImage = cv::Mat(ptrGrabResult->GetHeight(),ptrGrabResult->GetWidth(),CV_8UC1,din).clone(); return cvImage; }
在采集线程中发出的信号,在UI线程就要有对应的槽函数。
/* 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& mat); cv::Mat QImage2Mat(QImage image); private slots: //显示图像的槽函数 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()<<"耗时"<<ElapsedTimer.elapsed()<<"毫秒"; 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 &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\n", mat.depth(), mat.channels()); qWarning() << "cvMatToQImage - cv::Mat image type not handled in switch:" << mat.type(); break; } return QImage(); }
这里就是第一种信号槽的方法,通过emit ThreadPic(MatImg)发送信号,在UI线程通过槽函数Thread_Img(cv::Mat img)来接收Mat类型的图像进行显示,这里Mat类型不是Qt的元数据,所以要使用qRegisterMetaType(“Mat”)来进行注册。
如果我创建了一个数据类来保存图像处理时的数据,在图像采集的时候要讲采集的图像放到数据类里,UI线程还会设置不同的变量参数也要放到数据类里,在处理线程要使用数据的时候就需要去数据类去读取数据,这么多类同时去读写,如何才能实现同步共享,这里就需要在UI线程创建各个类之后进行指针的共享。
m_ImgProcessObj = new ImgProcessThread();
//初始化数据类
currentData = new MyData();
m_Product = new productManager(this);
m_Product->GetMyDataPoint(currentData);
m_ImgProcessObj->GetMyDataPoint(currentData);
这里使用GetMyDataPoint这个函数将数据类指针共享给其他类的需要调用数据的指针,其实就是两个指针指向同一内存地址。
void ImgProcessThread::GetMyDataPoint(MyData *DPoint)
{
DataPoint = DPoint;
}
这样在图像处理类里就可以用DataPoint 这个指针自由的调用数据类的成员变量和函数了,当然这里要引用数据类的头文件。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。