5

I'm writing a simple Qt program to capture video feed from camera (using OpenCV). I'm using a QThread object that loops, capturing images and feeding them to the MainWindow object. This is working as it should.

The problem is that when I close, the application (i.e. pressing the "X") the camera capturing thread stops and the gui disappears. But the program is still running in the background. I also get a warning in the application output saying :

QThread: Destroyed while thread is still running.

How can I stop the application completely when quitting it?

main.cpp

#include <QApplication>
#include "application.h"

using namespace cv;

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    Application app;
    app.init();

    return a.exec();
}

application.h

#include "mainwindow.h"
#include "camerathread.h"
#include "mathandler.h"
#include "tools.h"
#include "opencv2/core/core.hpp"

#ifndef APPLICATION
#define APPLICATION

class Application : public MatHandler{
    MainWindow w;
    CameraThread ct;
public:
    Application() {
        w.setFixedSize(800,600);
    }

    void init() {
        ct.setMatHandler(this);
        ct.start();
        w.show();
    }

    void handleMat(cv::Mat mat) {
        QImage qImage = toQImage(mat);
        w.setImage(qImage);
    }
};

#endif // APPLICATION

camerathread

#include <QThread>
#include "mathandler.h"
#include "opencv2/highgui/highgui.hpp"

#ifndef CAMERATHREAD
#define CAMERATHREAD

class CameraThread : public QThread {
    MatHandler *matHandler;
public:
    ~CameraThread() {
    }

    void setMatHandler(MatHandler *h) {
        matHandler = h;
    }

private: void run() {
        cv::VideoCapture vc(0);

        if (vc.isOpened()) {
            for(;;) {
                cv::Mat img;
                vc >> img;
                matHandler->handleMat(img);
            }
        }
    }
};

#endif // CAMERATHREAD

The program consists of more code than this, but I only included the code I think is relevant for the question. I'll post the rest if necessary.

birgersp
  • 3,909
  • 8
  • 39
  • 79
  • Check to see if there are other threads running besides the UI and camera thread. It's possible that the APIs you're using spawn other threads. – Paul Sasik Feb 01 '15 at 18:30
  • I'm not sure how to check for this... Could you please explain? – birgersp Feb 02 '15 at 19:05
  • Checking on running threads depends on the IDE you're using (if it's worth its salt). For example, this search: https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=how%20to%20see%20running%20threads%20in%20eclipse yielded this result: http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Ftasks%2Fanalyzingthreads.html But again, it depends on your IDE. I just demonstrated a sample view /w Eclipse. – Paul Sasik Feb 02 '15 at 19:15

1 Answers1

3

There are some major problems with your code. First of all in your run function the unending for loop will cause your thread to never stop unless you manage thread termination by your self like :

while(!finishThread)
{
   cv::Mat img;
   vc >> img;
   matHandler->handleMat(img);
}

You should set finishThread to true when the application is going to close. Just provide a slot in which you set the value for finishThread. When you want to terminate the thread emit a signal that is connected to that slot with a true value. After that wait for the thread to finish properly for some seconds and force it to terminate if it did not finish :

emit setThreadFinished(true); //Tell the thread to finish
if(!ct->wait(3000)) //Wait until it actually has terminated (max. 3 sec)
{
    ct->terminate(); //Thread didn't exit in time, probably deadlocked, terminate it!
    ct->wait(); //We have to wait again here!
}

Also you should not directly call handleMat function from an other thread. It may cause your application crash or lead to undefined behaviour. For that use signal/slot mechanism. Connect a signal from your thread to that slot and emit the signal along the argument each time you want to call it.

An other point is that you are better to derive your class from QObject and use moveToThread. You can do this in the constructor of your class:

th = new QThread();

this->setParent(0);
this->moveToThread(th);

QObject::connect(th,SIGNAL(started()),this,SLOT(OnStarted()));
QObject::connect(th,SIGNAL(finished()),this,SLOT(OnFinished()));

th->start();

Your initialization and termination tasks should be done in OnStarted() and OnFinished() slots respectively. You can have a worker function in which you run the repetitive operation.

Also in the destructor of your Application class quit the thread in some way like the one stated.

Nejat
  • 31,784
  • 12
  • 106
  • 138
  • Thanks for the feedback, it's dearly needed as I am (as you have probably realized already) a newbie in C++ and Qt programming. But one thing remains unclear to me. Why should I use a public slot in CameraThread when I can use a public void method for stopping the application? What are the pros/cons? – birgersp Feb 02 '15 at 13:59
  • 1
    If you use a method to stop the application then you have to directly call it like `ct->setFinished(true);` from `Application` which is really dangerous. That's because they are in two different threads and accessing a variable from two threads simultaneously leads to undefined behavior. When you use the signal/slot mechanism to stop the thread, you make sure that the variable is only accessed from one thread (`CameraThread` here). Never ever directly call a function of an object which is in an other thread. – Nejat Feb 02 '15 at 14:16
  • Noted, thanks. On the main issue; I added the ct->stop code to the Application deconstructor like you suggested. Also added QThread::exit() in the deconstructor of Thread. Application is still running after i close the main window... – birgersp Feb 02 '15 at 16:22
  • Do you check the termination condition in run function? Do you set the termination condition to true in destructor and wait for finishing? – Nejat Feb 02 '15 at 16:30
  • Yes, the run function ends as the while-loop breaks. But the application thread waits infintely for the camera thread to finish... http://pastebin.com/P2SA7YDD – birgersp Feb 02 '15 at 16:33