Qthreads从不关闭自己,Infinite EventLoop

时间:2014-08-07 17:07:43

标签: c++ multithreading qt opencv

大家好我用Qt制作了一个程序,可以并行使用网络摄像头(每个网络摄像头分配到一个不同的线程,我检索流)。我的目标是能够以固定的时间间隔从我的所有网络摄像头拍摄照片。例如,每秒所有网络摄像头都会拍照并将其保存到计算机中。

但是我在使用QThreads时遇到了问题。该程序完成我所描述的,但我的线程永远不会死,所以我的程序在执行结束时不会关闭。为了理解和构建我的Qthread代码,我使用了这个articleone(对不起,它是法语,但它与第一​​个大致相同)。

我让Gui问用户:

  • 保存图片的路径
  • 网络摄像头数量
  • 每张照片之间的间隔拍摄
  • 每个网络摄像头的照片数量
  • 用于启动线程并检索流的“确定”按钮

我的程序使用2个类:

  • 序列(QWidget):这是我的UI线程,并从用户检索上面的所有infiormations以将其传递给网络摄像头
  • 网络摄像头(QObject):控制网络摄像头。我用OpenCV来做这个

一旦用户输入了所有信息,他就会点击连接到SLOT的名为launch的OK按钮。 这是我简化的代码:

Sequence.h

class Sequence : public QWidget
{
    Q_OBJECT

public:
    Sequence(QWidget *parent = 0);
    void InitialisationGUI();
    ~Sequence();
private:
    QGroupBox* path;          // This is for layout organisation puspose   
    QLineEdit* main_folder;   // Holds the Global path for all the Folders where the photos will be 
    QLineEdit* fruit;         //Folder where you want to save this sequence of photos
    QLineEdit* cam;           // holds the number of webcams the user entered
    QLineEdit* intervalle;    // holds the timelapse between each take
    QLineEdit* nombre;        // holds the number of photo per webcam

    QVector<QThread*> threads;  // vector of threads
    QVector<Webcam*> webcams;   // vector of webcam

public slots:
    void ExplorerOpener();     // Opens a window for the user to easily choose his folder
    void PhotoTakerLauncher();  // Creates the objects, threads and launch it all

};

Sequence.cpp

#include "sequence.h"

Sequence::Sequence(QWidget *parent)
    : QWidget(parent)
{
    threads = QVector<QThread*>();
    webcams = QVector<Webcam*>();

    InitialisationGUI();
}

Sequence::~Sequence()
{

}

void Sequence::InitialisationGUI()
{

    // Here is the Initialisation of all the GUI Elements, but taking too much lines to put in here

    // Launch button OK
    QPushButton *launch = new QPushButton("OK");

    // Organisation of the widget layout
    QGridLayout *formulaire = new QGridLayout;
    formulaire->addWidget(launch,5,2,Qt::AlignRight);

    setLayout(formulaire);

    //Connection Signaux Slots
    connect(launch,SIGNAL(clicked()),this,SLOT(PhotoTakerLauncher()));


}  

void Sequence::PhotoTakerLauncher()
{
    // For each webcam i create an object webcam and a thread
    for(int i=0; i<cam->text().toInt();i++)
    {
        // As the number of webcam is an unknow variable i came up with this way of doing but i am afraid it may also be the cause of my problem

        // I create a new thread and append it into my vector
        threads.append(new QThread);

        // does the same for my webcam object
        webcams.append(new Webcam(0,main_folder->text() + "/" + fruit->text(), i,intervalle->text(),nombre->text()));

        // i move my object into the thread for the operation not to be in my UI Thread
        webcams[i]->moveToThread(threads[i]);


        // Connection For Initialisation and Termination of the Thread
        connect(threads[i],SIGNAL(started()),webcams[i],SLOT(Streamer()));

        // THOSE ARE THE CONNECTIONS THAT DOESN'T REACT WHEN MY PROGRAM IS FINISHED
        connect(webcams[i],SIGNAL(finishedSequence()),threads[i],SLOT(quit()));
        connect(webcams[i],SIGNAL(finishedSequence()),webcams[i],SLOT(deleteLater()));
        connect(threads[i],SIGNAL(finished()),threads[i],SLOT(deleteLater()));

        threads[i]->start();
    }


    // This is not important i used it for debug it is what assured me that none of my threads were quitting
    for(int i=0;i<threads.count();i++)
    {
       /* threads[i]->wait();
        cout << "Thread de caméra numéro: " + QString::number(i + 1).toStdString() << endl;
        delete webcams[i];*/
    }
}

webcam.h

class Webcam : public QObject
{
    Q_OBJECT
public:
    explicit Webcam(QObject *parent = 0);
    Webcam(QObject *parent = 0,QString path= NULL, int number = 1, QString time = "1000", QString count = "100");
    bool getStreaming();
    void StopStreaming();
    void test();

signals:
    void finishedSequence();  // the signal I emit once all my photos are taken

public slots:
    void ImagesRetriever(); // slot, called by a timer, which saves the image streaming
    void Streamer();        // Retrieve the stream of the webcam and initializes the timer

private:
   // CvCapture* capture;  // old way
    VideoCapture capture;  // Holds the stream of the webcam
    Mat frame;     
    string window_name = "Capture";
    bool streaming;
    QDir folder;     // path to save the image
    int iterator;   // to number the image
    QTimer* timer;

    int intervalle;  // the timelapse between each take
    int nombre_photo; // number of photo
    int nombre_cam; // it is the number of this camera

    QString folder_path;

};

webcam.cpp

Webcam::Webcam(QObject *parent, QString path, int number, QString time, QString count)
    : QObject(parent)
{
    // Initialization of variables needed
    folder_path = path;
    nombre_cam = number;
    intervalle = time.toInt();
    nombre_photo = count.toInt();
    streaming = false;
    iterator = 1;

    timer = new QTimer(this);
    std::cout << "Initialisation Complete"<< std::endl;
}

// Slot launched once the thread has started
void Webcam::Streamer()
{
    capture.open(nombre_cam);
    if(capture.isOpened())
    {
        streaming = true;

        // Connects on timeout 
        connect(timer,SIGNAL(timeout()),this,SLOT(ImagesRetriever()));
        timer->start(intervalle);
    }
    else
    {
        // Emit the finished signal which should kill the thread but doesn't
        emit finishedSequence();
    }
}

void Webcam::ImagesRetriever()
{
    // I stop and then relaunch the timer every time because the image saving can take up some time and for short timers it can create some conflict
    timer->stop();

   // Check that the stream is sill opened
    if(capture.isOpened())
    {
        capture >> frame;

        // check that an image has been retrieved
        if(!frame.empty())
        {
            // Saving the image 
            QString image_path = folder_path + "/imagecam" + QString::number(nombre_cam + 1) + "_" + QString::number(iterator) + ".jpg";
            imwrite(image_path.toStdString(),frame);
            iterator ++;
        }
        else
        {
            cout << "Image non Récupérée -- Break"<<endl;
        }
    }

    // while we still have photos to take
    if(iterator <= nombre_photo)
    {
        timer->start(intervalle);
    }
    else
    {
       // we emit the signal which cause me PROBLEMS
       emit finishedSequence();
    }
}

我试图解释这一切,但如果你认为我做得不好,请不要犹豫。

我对为什么我的线程没有被杀死感到有点无能为力。目前我的想法是,由于QVector,Qt无法保存我的信号参数的副本以将其传递到插槽。但由于我对此不太熟悉,我无法说出来。

我的另一个想法是,可能视频流没有被杀死,因此对象和线程无法被杀死,但看到API我的印象不是。

有什么想法吗?

编辑1:回答我的线索问题

我终于找到了如何解决我的问题,我有很好的方法,但我的信号已完成序列连接到太多的插槽,需要按顺序完成,而不是按随机顺序。

首先,我向网络摄像头课程添加了一个新信号:

signals:
    void finishedSequence();
    void destroyedWebcam();

并修改默认析构函数以在代码中添加我的新信号:

Webcam::~Webcam()
{
    cout << " We are in the destructor" << endl;
    emit destroyedWebcam();
}

然后我将sequence.cpp中的连接更改为:

connect(threads[i],SIGNAL(started()),webcams[i],SLOT(Streamer()));
connect(webcams[i],SIGNAL(destroyedWebcam()),threads[i],SLOT(quit()));
connect(webcams[i],SIGNAL(finishedSequence()),webcams[i],SLOT(deleteLater()));
connect(threads[i],SIGNAL(finished()),threads[i],SLOT(deleteLater())); 

然后我在我的Sequence类中添加了一个Slot来清除所有线程和网络摄像头对象,一旦它们全部完成,以便能够再次启动它:

void Sequence::Reinitialization()
{
    cout << "Thread Destroyed => Reinitialization of threads and webcams" << endl;
    count ++;
    if(count == cam->text().toInt())
    {
        threads.clear();
        webcams.clear();
        cout << "QVectors cleared" << endl;
        count =0;
    }
}

最后我添加了一个新连接:

connect(threads[i], SIGNAL(destroyed()),this,SLOT(Reinitialization()));

这对我来说非常适合2个网络摄像头。现在我可以重复该程序,而不必每次都杀死它。因此,如果您想使用这种方式进行线程,那么它可以正常工作。

然而,我正面临一个新问题。当我连接3个网络摄像头时,我只能检索2,当3个网络摄像头中的一个是我的集成网络时会执行。我将搜索为什么以及如何解决此问题。

编辑2:对相机问题数量的回答

这是一个很大的问号,为什么我不能在我的电脑上使用3个网络摄像头知道我有两个USB控制器,而且usb速度足以在同一总线上支持两个。经过研究,我发现我的一个控制器专用于我的集成网络摄像头,而不是我的任何USB端口使用。所以最后我只有一个控制器和3个网络摄像头的总线。我所做的就是检索所有3个是降低图像的分辨率(到720p)并将帧速率降低到7fps,之后它运行良好。

我的项目终于完成了。希望我的反思,研究和答案能够帮助他人。

0 个答案:

没有答案