没有调用QThread :: start()就可以使用QThread吗?

时间:2014-03-26 11:10:13

标签: c++ multithreading qt opencv

有一段时间我正在使用Qt处理一个应用程序,我必须从相机中抓取帧。相机将以与应用程序其余部分不同的线程运行。我遵循了以下建议:

http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/

不要继承QThread。相反,我创建了一个工作对象CCameraCapture并将其移动到QThread。相机的帧抓取由QTimer控制,QTimer连接到抓斗框槽。将CCameraCapture移动到QThread后,可以通过启动计时器来启动捕获。我的问题是:我是否必须调用QThread类的start()?我的方法无需调用即可。循环由计时器执行,工作人员实际上在另一个线程中工作而不调用start。所以我想知道如果使用定时器,调用start()是否有任何意义?为清楚起见,我将下面的Worker类放在主应用程序中:

m_CameraCaptureThread= new QThread();
m_CameraCapture = new CCameraCapture(30);
m_CameraCapture->moveToThread(m_CameraCaptureThread);

//Connect error signal
QObject::connect(m_CameraCapture, SIGNAL(error(QString,QString)), this, SLOT(reportError(QString,QString)));

//Connect started() of QThread to the starting function of the worker class
connect(m_CameraCaptureThread, SIGNAL(started()), m_CameraCapture, SLOT(startGrabbing()));

//Connect the finished signal of the worker class to the thread for quitting the loop
connect(m_CameraCapture, SIGNAL(finished()), m_CameraCaptureThread, SLOT(quit()));

//This connections guarantees that the *m_CVideoCapture is automatically deleted if the event loop of the thread is terminated. Therefore, m_CVideoCapture does not need to be released manually if the capturing process is stopped.
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCaptureThread, SLOT(deleteLater()));
QObject::connect(m_CameraCapture, SIGNAL(finished()), m_CameraCapture, SLOT(deleteLater()));

//Connect sendFrame to update frame for displaying the current frame
QObject::connect(m_CameraCapture, SIGNAL(sendFrame(QImage)), this, SLOT(receiveFrame(QImage)));
/** 

所以到目前为止还没有调用m_CameraCaptureThread-> start()来启动处理循环。但是,如果我调用CameraCapture-> startGrabbing(),它将很好地工作。 grabFrame()插槽由计时器触发,帧被发送到主应用程序。但是,到目前为止我看到的所有代码示例都称为start(),即使它们使用了计时器:例如: http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-1/(页面末尾的用法2-1)

这是我的CCameraCapture.h:

/** 

@file CCameraCapture.h 
@brief this file contains the definition of the class CCameraCapture.


**/ 

#ifndef CCameraCapture_H
#define CCameraCapture_H

//Required Qt libs
#include <QObject>
#include <QImage>
#include <QTimer>
#include <QString>
#include <QMutex>
#include <QDebug>

//Required OpenCV libs
#include <opencv2\opencv.hpp>

/** 
@class CCameraCapture 

@brief This class defines a video capture object which should be moved to thread.

class CCameraCapture : public QObject{

Q_OBJECT

public:
    /** 
    @brief Constructor of CCameraCapture.
    @param frameRate Desired frame rate (if possible)
    */
    CCameraCapture(double frameRate=30);
    /**
    @brief Destructor of CCameraCapture.
    */
    ~CCameraCapture();
    /**
    @brief This function terminates the thread. 
    */
    void exitThread();
    bool startGrabbing();
    void stopGrabbing();
    /**
    @brief Check if camera is running
    @return Returns true if camera is running
    */
    bool isGrabbing();

private:
    //The openCV capturing object to access the camera
    cv::VideoCapture m_Cap;
    // Device index
    int m_IdxDevice;
    // Timer for triggring grab frame
    QTimer m_Timer;
    //The most recent frame
    QImage m_Frame;
    //Mutex to lock variables
    QMutex m_Mutex;

private slots:
    /**
    @brief This slot grabs a frame from the camera. It is triggered by the timer m_Timer.
    */
    void grabFrame();

signals:
    /**
    @brief This signal needs to be connected to the slot in the main application which should receive the images.
    @param img The most recent frame.

    */
    void sendFrame(QImage currentFrame);
    /** 
    @brief This signal is emitted if an error occurs
    @param errMsg QString contains the error message to be displayed.
    @param errTitle QString contains the title of the diplayed error message.

    This signal should be connected to a slot in the main application. It allows to send error reports back to the main application which can be displayed on screen
    */
    void error(QString errMsg,QString errTitle);
    void finished();
};
#endif //CCameraCapture_H

这是cpp文件:

/**
@file CCameraCapture.cpp 
@brief this file contains the function definitions of CCameraCapture.
**/ 

#include "CCameraCapture.h"

CCameraCapture::CCameraCapture(double frameRate):m_Mutex(),m_IdxDevice(0)
{
    //Connect timer to grabFrame
    connect(&m_Timer, SIGNAL(timeout()), this, SLOT(grabFrame()), Qt::DirectConnection);
    //Set framerate
    m_Timer.setInterval(1000/frameRate);
}


CCameraCapture::~CCameraCapture(void)
{
}

void CCameraCapture::grabFrame(){
    qDebug() << "Worker thread ID" << this->thread();
    //Lock this function
    QMutexLocker ml(&m_Mutex);
    //Local image storage
    cv::Mat cvFrameBGR,cvFrameRGB;
    //Get new frame from camera
    m_Cap>>cvFrameBGR;
    //Convert frame to RGB
    cv::cvtColor(cvFrameBGR, cvFrameRGB, CV_BGR2RGB);
    //Convert cv::Mat to QImage
    QImage m_Frame=QImage((uchar*)(cvFrameRGB.data),cvFrameRGB.cols,cvFrameRGB.rows,QImage::Format_RGB888);     
    //Send frame to receivers
    emit sendFrame(m_Frame);
}

bool CCameraCapture::startGrabbing(){
    //Lock this function
    QMutexLocker ml(&m_Mutex);
    //Check if camera is open
    if(!m_Cap.isOpened()){
        //Connect to camera
        if(!m_Cap.open(m_IdxDevice)){
            emit error(QString("Could not connect to Camera."),QString("Error: No camera detected"));
            return 0;
        }
        else{
            //Start grabbing
            m_Timer.start();
            return 1;
        }
    }
    else{
        //Start grabbing
        m_Timer.start();
        return 1;
    }
}

void CCameraCapture::stopGrabbing(){
    //Lock this function
    QMutexLocker ml(&m_Mutex);
    //Stop grabbing
    m_Timer.stop();
}

bool CCameraCapture::isGrabbing(){
    //Lock this function
    QMutexLocker ml(&m_Mutex);
    //Return true if timer is running and triggering grabFrame
    return m_Timer.isActive();
}

void CCameraCapture::exitThread(){
    //Lock this function
    QMutexLocker ml(&m_Mutex);
    //Stop grabbing
    stopGrabbing();
    //Release camera
    m_Cap.release();
    //Emit finished signal which should be connected to quit() of QThread and deleteLater() of this class;
    emit finished();
}

2 个答案:

答案 0 :(得分:0)

我怀疑它没有在新线程上运行,如果没有调用start。

虽然您可能不需要事件循环(尽管QTimer会这样),但Qt documentation表示: -

  

通过调用run()开始执行线程。操作系统将根据优先级参数调度线程。如果线程已在运行,则此函数不执行任何操作。

所以,你应该调用start,但是如果它已经运行那么再次调用就没什么坏处。

通过在将QObject的thread()函数移动到新线程并比较返回的指针之前和之后调用QObject的thread()函数,可以测试运行该对象的线程。

答案 1 :(得分:0)

好的,我想我找到了重点,为什么我不必调用start()。对于我使用的计时器

connect(&m_Timer, SIGNAL(timeout()), this, SLOT(grabFrame()), Qt::DirectConnection);

直接连接将在没有启动事件循环的情况下工作。如果我将其更改为

connect(&m_Timer, SIGNAL(timeout()), this, SLOT(grabFrame());

我必须致电

m_CameraCaptureThread->start();

在主应用程序中使其正常工作。