为什么要使用多线程
最常见的情况是,我需要使用一个子线程处理一个子任务,然后将子任务的运行结果显示在主线程MainWindow上。若只使用主线程进行计算,那么在运行这个子任务的时候,MainWindow的其他任务是无法运行的。如果我想一个Mainwindow同时显示两个摄像头,是无法完成的。又或者是子任务需要等待一段时间(sleep等),MainWindow便会无响应。
跟C++11中很像的是,Qt中使用QThread来管理线程,一个QThread对象管理一个线程,在使用上有很多跟C++11中相似的地方,但更多的是Qt中独有的内容。另外,QThread对象也有消息循环exec()函数,即每个线程都有一个消息循环,用来处理自己这个线程的事件。
废话不多说,直接用一个开发样例进行讲解。
任务目的
使用QThread在子线程使用opencv采集摄像头,并在MainWindow实时显示。
基本思路
- 在主线程设置一个触发器,触发以后运行子线程
- 子线程调用OpenCV采集摄像头,将采集的Mat转化成QImage
- 主线程发现采集到了QImage,更新主线程的UI
伪代码
//主线程
//第一步,在主线程设置一个触发器,触发以后运行子线程
connect(操作界面上的按钮,按钮抬起,this,触发子线程的方法);
//第二步,子线程调用OpenCV采集摄像头,将采集的Mat转化成QImage
子线程 = new 子线程(this);
//第三步,主线程发现采集到的了QImage,更新主线程的UI
connect(子线程,SIGNAL(获取到了QImage), this,SLOT(更新UI的方法));
//子线程
void 子线程名::run() //overwrite
{
永远
{
摄像头开关为true:采集();
摄像头开关为false:,将开关设置为true然后break;
}
}
void 子线程名::采集()
{
如果没有检测到摄像头:摄像头开关设置为false,回收资源
如果检测到摄像头:采集到Mat里,转化为QImage,提醒主线程采集到QImage了
}
注意事项
子线程:
使用emit()来提醒主线程获取到了新的资源。emit函数调用的方法应声明在.h头函数的signals分类中。
主线程:
注意线程的调用以及回收。
关键部分代码:
mainwindow.cpp
//left camera stream thread
connect(ui->pushButton_leftStream, &QPushButton::released, this, &MainWindow::on_pushButton_leftStream_released);
left_thread = new LeftCameraThread(this);
connect(left_thread, SIGNAL(captured(QImage)), this, SLOT(updateLeftGraphics()));
//capture current stream----left
void MainWindow::on_pushButton_leftStream_released()
{
left_thread->start();
}
//update graphicsViewLeft
void MainWindow::updateLeftGraphics()
{
QGraphicsScene *leftScene = new QGraphicsScene;
leftScene->addPixmap(QPixmap::fromImage(leftDiskImage).scaled(645,381, Qt::KeepAspectRatio));
ui->graphicsViewLeft->setScene(leftScene);
ui->graphicsViewLeft->show();
}
leftCameraThread.cpp
#include "LeftCameraThread.h"
#include "mainwindow.h"
#include <opencv2/opencv.hpp>
#include <opencv2/videoio.hpp>
#include <iostream>
#include <QGraphicsScene>
using namespace cv;
using namespace std;
//SDK
extern bool leftCamSwitch;
extern Mat leftFrame;
extern QImage leftDiskImage;
VideoCapture capture0(0);
LeftCameraThread::LeftCameraThread(QObject *parent) : QThread(parent)
{
}
void LeftCameraThread::run()
{
forever
{
if(leftCamSwitch) //default is on
{
LeftCameraThread::captureProcess();
}
else //or off
{
leftCamSwitch = true;
break;
}
}
}
void LeftCameraThread::captureProcess()
{
if (!capture0.isOpened()) //if no camera connection
{
cout << "No video stream detected" << endl;
capture0.release();
leftCamSwitch = false;
}
else
{
capture0 >> leftFrame;
cvtColor(leftFrame, leftFrame, COLOR_BGR2RGB);
leftDiskImage = QImage((const unsigned char*)leftFrame.data, leftFrame.cols, leftFrame.rows, leftFrame.step,QImage::Format_RGB888);
emit captured(leftDiskImage);
}
}
leftCamera.h
#ifndef PALLETIZINGCAMERA_LEFTCAMERATHREAD_H
#define PALLETIZINGCAMERA_LEFTCAMERATHREAD_H
#include <QThread>
#include <QImage>
#include <QObject>
#include <QDebug>
class LeftCameraThread : public QThread
{
Q_OBJECT
public:
explicit LeftCameraThread(QObject *parent = 0);
public:
void run();
void captureProcess();
signals:
void captured(QImage image);
public slots:
private:
};
#endif //PALLETIZINGCAMERA_LEFTCAMERATHREAD_H
文章评论