Using Threads without subclassing
-
Hello everybody!
Some weeks ago I started working with Qt and I am happy with this great framework! Now, I am working at a university-project using Microsoft's Kinect to generate pointclouds/meshes in realtime. In this context I have to work with threads (which is my first time using them) and I have some basic problems.
I work under Linux Mint 11/Ubuntu 11.04 using Qt Creator 2.1.0, Qt 4.7.2 and the great Kinect-interface libfreenect of the "OpenKinect-project":http://www.openkinect.org.
I tried a lot and want to follow the "threading-idea" without subclassing, explained at "here":http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/. But it seems I do something wrong or maybe misunderstand the use of threads, why I need help. Also searching the web didn't help.
"Here":http://dl.dropbox.com/u/643465/klassendiagramm_klein.png is a pseudo-class-diagram showing the important classes and data-flow.
!http://dl.dropbox.com/u/643465/klassendiagramm_klein.png(Class-Diagram)!
Basically, there are three QGLWidget subclasses presenting data: RGBWidget (displaying the Kinect's RGB-image), DepthWidget (displaying the Kinect's depth-image) and a MeshWidget (displaying a pointcloud reconstructed from the rgb- and depth-image).
The ImageController owns a timer, which runs with an interval of 0 msecs. It grabs the RGB- and depth-image of the Kinect and forwards them to the RGBWidget and DepthWidget. And it should forward them to the Reconstructor, which creates a pointcloud and colors (of the points) and forwards them to the MeshWidget. Unfortunately the reconstruction needs some time, why I want to move this operation to a thread.
I followed "this":http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/ explanation and implemented the ImageController and Reconstructor as follows:
imagecontroller.h
@
#include <QObject>
#include <QTime>
#include <QThread>#include <vector>
#include "qkinect.h"
#include "rgbwidget.h"
#include "depthwidget.h"
#include "meshwidget.h"#include "reconstructor.h"
class ImageController : public QObject
{
Q_OBJECT
public:
explicit ImageController(QObject *parent, RGBWidget *rgbWidget, DepthWidget
*depthWidget, MeshWidget *meshWidget);private:
QKinect *kinect;Reconstructor recon;
QThread reconThread;RGBWidget *rgbWidget;
DepthWidget *depthWidget;
MeshWidget *meshWidget;QTime timer;
std::vector<uint8_t> rgbImage;
std::vector<uint8_t> depthImage;
std::vector<uint16_t> depthRaw;protected:
void timerEvent(QTimerEvent *event);
};
@imagecontroller.cpp
@
#include "imagecontroller.h"ImageController::ImageController(QObject *parent, RGBWidget *rgbWidget, DepthWidget
*depthWidget, MeshWidget *meshWidget) :
QObject(parent), recon()
{
// Do some init-stuff herestartTimer(0);
recon.moveToThread(&reconThread);
connect(&reconThread, SIGNAL(started()), &(this->recon), SLOT(startReconstruction()));
connect(&(this->recon), SIGNAL(finished()), &reconThread, SLOT(quit()));connect(&(this->recon), SIGNAL(newPoints(std::vector< std::vector<float> >)), this->meshWidget, SLOT(setPoints(std::vector< std::vector<float> >)));
connect(&(this->recon), SIGNAL(newColors(std::vector< std::vector<int> >)), this->meshWidget, SLOT(setColors(std::vector< std::vector<int> >)));
}void ImageController::timerEvent(QTimerEvent *event)
{
if (kinect->getRGBImage(rgbImage))
{
rgbWidget->setImage(rgbImage);if (!reconThread.isRunning())
{
recon.setRgbValues(rgbImage);
recon.setDepthValues(depthRaw);
reconThread.start();
}
}if (kinect->getDepthImage(depthImage))
{
depthWidget->setImage(depthImage);
}if (kinect->getDepthRaw(depthRaw))
{
if (!reconThread.isRunning())
{
recon.setRgbValues(rgbImage);
recon.setDepthValues(depthRaw);
reconThread.start();
}
}
}@
reconstructor.h
@
#include <QObject>
#include <QMetaType>
#include <QDebug>#include <vector>
#include <stdint.h>#include "YMLParser.h"
class Reconstructor : public QObject
{
Q_OBJECT
public:
explicit Reconstructor(QObject *parent = 0);private:
YMLParser yml;std::vector<uint8_t> rgbValues;
std::vector<uint16_t> depthValues;std::vector< std::vector<float> > points;
std::vector< std::vector<int> > colors;signals:
void newPoints(std::vector< std::vector<float> > points);
void newColors(std::vector< std::vector<int> > colors);void finished();
public slots:
void startReconstruction();void setRgbValues(std::vector<uint8_t> rgbValues);
void setDepthValues(std::vector<uint16_t> depthValues);
};#endif // RECONSTRUCTOR_H
@reconstructor.cpp
@
#include "reconstructor.h"Reconstructor::Reconstructor(QObject *parent) :
QObject(parent),
yml("/path/to/calib.yml")
{
// Do some init-stuff hereqRegisterMetaType< std::vector< std::vector<float> > >("std::vector< std::vector<float> >");
qRegisterMetaType< std::vector< std::vector<int> > >("std::vector< std::vector<int> >");
}void Reconstructor::startReconstruction()
{
// Do reconstruction hereemit newPoints(points);
emit newColors(colors);emit finished();
}void Reconstructor::setRgbValues(std::vector<uint8_t> rgbValues)
{
this->rgbValues = rgbValues;
}void Reconstructor::setDepthValues(std::vector<uint16_t> depthValues)
{
this->depthValues = depthValues;
}@
Unfortunately, the performance of my program doesn't get better and it seems that I did something wrong. I have no idea how to go on and would be very thankful for any hint.
Btw: Most likely, I did some beginner-mistakes independent from the threading-topic. Any suggestion for improvement is welcome!
Regards,
Daniel -
If you are only using a single QThread and you want events to execute within the QThread itself, you have to start an event loop. Create a class and inherit a QThread, and in the custom protected function run() call exec(). This will allow for your events to be triggered. When you want to exit the event loop, call quit()
so in your QThread class
@
QThreadClassName::run()
{
.. // do anything
.. // do anything
.. // do anything
exec(); // start the event loop
}
@ -
Hello,
thanks for your reply. I'll try "QThread-subclassing-approach" tomorrow again. But I wanted to avoid it since I read "The great QThread Mess":http://www.christeck.de/wp/2010/10/23/the-great-qthread-mess/ and "You're doing it wrong":http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/, where the "moveToThread-approach" is explained and recommended. That's why I wanted to implement it.
I think it's easier and shorter to realize and clearly separates the computation from the thread.
But I'll give it a try tomorrow.
Any other suggestions, why my approach not works?
Regards,
Daniel -
It's simple illustration how it can be used.
@
class Worker : public QObject {
Q_OBJECTpublic:
Worker(QObject *parent = 0) : QObject(parent) {}
~Worker() {}public slots:
void doSomeWork()
{
emit finished();
emit workResult();
}
void doAnotherWork()
{
emit finished();
emit workResult();
}signals:
void workResult();
void finished();
};
class MyWidget : public QWidget {
Q_OBJECTpublic:
MyWidget(QWidget *parent = 0) : QWidget(parent), timerId(-1)
{
worker = new Worker; //without parent
workerThread = new QThread; //you can set parentworker->moveToThread(workerThread);
workerThread->start(); //start eventlooptimerId = startTimer(/interval/);
connect(worker, SIGNAL(workResult()), this, SLOT(workResult())); //Qt::QuededConnection
connect(worker, SIGNAL(finished()), this, SLOT(finished())); //Qt::QuededConnectionconnect(this, SIGNAL(doAnotherWorkr()), worker, SLOT(doAnotherWorkr())); //Q::QuededConnection
}
~MyWidget()
{
if (timerId != -1)
{
killTimer(timerId);
timerId = -1;
}workerThread->quit();
workerThread->wait();delete worker;
delete workerThread;
}public slots:
void workResult()
{
//do something
}
void finished()
{
//do something
}private:
Worker *worker;
QThread *workerThread;int timerId;
void timerEvent(QTimerEvent*)
{
if ( someWork )
{
//you can
//1: QMetaObject::invokeMethod(worker, "doAnotherWork"); //for qued
/2:/ emit doAnotherWorkr(); //for qued
//3: worker->doAnotherWorkr(); //for direct, or set connection to Qt::DircetConnection end emit signal doAnotherWork();
}
}signals:
void doAnotherWorkr();
};
@ -
Thank you for your reply, stima_ua.
It seems that my thread works now, but nevertheless my QGLWidgets "stops" in intervalls. So it seems that the general structure of my program might be wrong... :o(
Is it generally possible to render multiple QGLWidgets simultaneously/in realtime? Or do I have to use multiple GL-contexts? Or should the Widgets itselfs run in different threads?
Is it better to let the Widgets fetch the Images or to deliver them from a "ImageContoller", as I did?