2014-03-26 35 views
1

一段时间以来,我现在正在与一个应用程序的Qt合作,我必须从相机抓取帧。相机将运行在与应用程序其余部分不同的线程中。我跟着的建议:不使用QThread :: start()而使用QThread是否有意义?

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控制。将CCameraCapture移到QThread后,可以通过启动定时器来启动捕获。我的问题是:我必须调用QThread类的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->开始()被调用开始处理循环。但是如果我调用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(); 
} 

回答

0

我怀疑这是不是新的线程运行时,如果开始没有被调用。

虽然你可能不需要事件循环(虽然QTimer会),在Qt documentation状态: -

通过调用运行开始线程的执行()。操作系统将根据优先级参数调度线程。如果线程已经运行,这个函数什么都不做。

所以,你应该打电话给start,但是如果它已经在运行,那么再次调用它是没有害处的。

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

+0

感谢您的答案 - 我检查了CCameraCapture :: grabFrame(),如果它真的是一个不同的线程,因为我担心只有start()会实际启动一个不同的线程。它返回不同的线程ID。因此,我会假设我不必调用start(),因为我不需要事件循环。所以我想知道是否我错过了一些重要的事情,如果我不打电话开始除了有一个空的事件循环,或者如果我应该改变设计。 – 00Jan00

+0

我建议你无论如何称呼它。即使您的代码现在可以工作,您也可以决定使用该线程做其他事情,然后想知道为什么它不能正常工作,忘记启动它。你会发现不同线程上的对象之间的消息需要在每个线程上有一个事件循环。 – TheDarkKnight

+1

好点。直接连接使我的代码正常工作(请参阅下面的答案)。 – 00Jan00

0

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

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

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

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

我要叫

m_CameraCaptureThread->start(); 

在主应用程序得到它的工作。