How to use threads in qt - c++

I am new to QT. I need to use threads for some purpose. I searched a lot about threading in QT but all the articles and videos are using same example. They are using dialog and putting a label with 2 buttons to print some data on label. I want to use threads with MainWindow. My application include reading a serial data and then displaying the related information on a label. That information contains a string and an audio file. String and audio file needs to be played at the same time. I have a connected a signal for serial read like below:
connect(&Serial, SIGNAL(readyRead()), this, SLOT(SerialRead()));
QString MainWindow::SerialRead()
{
word Words; //
QString serialData = Serial.readAll(); //Reading Serial Data
//Now here I want to start the two threads
//Thread 1 to display string
//Thread 2 to play audio
return 0;
}
How can I achieve above task. Can anyone please refer me to some usefull links or articles. Thanks

While I very highly recommend that you use std::thread instead of QThread, it's your call. However, on the Qt docs page of QThread there's a very good example that exactly fits what you need. Here it's:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString &parameter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
Basically, in this example, the Controller is your MainWindow, and the constructor of Controller is your MainWindow::SerialRead(). Be careful with memory and thread management though if you want to do that, because that Controller is made to destroy things when it exists, not when the thread is finished.
So you either use that controller as is (simply instantiate it in your MainWindow::SerialRead()), or change it to include parts of it in your MainWindow.

you may not need to use 2 threads to do such things. just emit a signal connected to setText(const QString&) and the other signal connected to the slot for playing audio. what is the size of serial data?

Related

QT slot not getting called on main thread

From the thread context of a slot in my QT GUI application (upon button push), I am trying to launch a worker thread to update other another part of the GUI with the results of a CPU intensive calculation - these results will update a table or a google like map widget - so this needs to occur in the main QT application thread where it is safe to update these widgets.
The problem I have is that the updateGUIWidget slot never gets called unless I change the connection type to Qt::DirectConnection - in which case it gets called in the worker thread (where it is unsafe to update the GUI). I checked the results of each of the connect calls and they are fine, it seems that there is some issue with the event loop somewhere. I'm not sure if I need to allocate the thread and the worker objects as members of the mainwindow or if its OK to do so from stack variables in the slot.
void
mainwindow::on_importSimulatedFlight_clicked()
{
// experimental worker thread model adapted from YouTube tutorial
// by Giuseppe di Angelo https://www.youtube.com/watch?v=BgqT6SIeRn4
// auto thread = new QThread;
// note worker created in gui thread here - we will move its thread
// affinity to 'thread' below before starting it.
auto thread = new QThread;
auto worker = new Worker;
connect(thread, &QThread::started, worker, &Worker::doWork);
// connect(worker, &Worker::progressUpdate, this, &mainwindow::updateGUIWidget, Qt::DirectConnection);
connect(worker, &Worker::progressUpdate, this, &mainwindow::updateGUIWidget, Qt::QueuedConnection);
connect(worker, &Worker::workDone, thread, &QThread::quit);
connect(thread, &QThread::finished, worker, &Worker::deleteLater);
// move worker to separate thread
worker->moveToThread(thread);
thread->start();
}
The mainwindow has a slots declared in mainwindow.h as follows:
class mainwindow : public QMainWindow
{
Q_OBJECT
public:
explicit mainwindow(QWidget *parent = Q_NULLPTR);
~mainwindow();
...
public slots:
void on_importSimulatedFlight_clicked();
void updateGUIWidget(const param& rParam);
...
}
and implemented in mainwindow.cpp as follows:
void
mainwindow::updateGUIWidget(const param& rParam)
{
... update widget components with rParam partial result here
}
and my worker is as follows:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork() {
const QString result;
for (int i=0; i<5; i++) {
const MMRTimedRecord foo;
// QThread::sleep(1);
emit progressUpdate(foo);
}
emit workDone(result);
}
signals:
void progressUpdate(const MMRTimedRecord&);
void workDone(const QString &result);
};
The reason it isn't working is because there's a serious flaw in your code: you are trying to emit a reference to a local variable to be handled in an slot on a different thread. That is a recipe for disaster.
When you are using Qt::QueuedConnection, you MUST emit by value, like this:
void progressUpdate(MMRTimedRecord val);
That means that your MMRTimedRecord must be copyable, and correspondingly, your slot must also accept by value. And why is there a mismatch between the signal progressUpdate(const MMRTimedRecord&) and the slot updateGUIWidget(const param& rParam); ?
You can check this answer for a possible solution. You can do
MainThreadEvent::post([&]()
{
// gui update stuff
}
);
in your slot to do the gui update in the main thread, but it is a crude approach to be sure. Despite this, I do something like this all the time. Be careful of dangling pointers and references (use QPointer)..., as the issued event is independent of the issuing object. Alternatively, use the timer approach.
It’s really easy – and you shouldn’t be managing any threads manually:
void Ui::slot() {
QtConcurrent::run([this]{
auto result = compute();
QMetaObject::invokeMethod(this, [this, r = std::move(result)]{
m_widget.setSomething(r);
});
});
}
The type of the data you compute should be movable.

Qt moveToThread, signals/slots with arguments

I've tried to use an approach from https://wiki.qt.io/QThreads_general_usage
with moveToThread. Everything is fine. But if I try to add the argument to the finished signal, there is the following problem:
class Worker : public QObject {
Q_OBJECT
public:
Worker();
~Worker();
public slots:
void process();
signals:
void finished(const std::string& value);
};
void Worker::process() { // Process. Start processing data.
// allocate resources using new here
qDebug("Hello World!");
std::string s = someFunctionReturningString();
emit finished(s);
}
The main class is:
class Main: public QObject {
Q_OBJECT
public:
void startProgram();
public slots:
void slotFinished(const std::string& s);
};
void Main::startProgram() {
QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &Worker::process);
connect(worker, &Worker::finished, thread, &QThread::quit);
connect(worker, &Worker::finished, worker, &Worker::deleteLater);
connect(worker, &Worker::finished, this, &Main::slotFinished);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void Main::slotFinished(const std::string& value) {
qDebug() << "result " << value.c_str();
}
If I connect this finished signal to some slot (slotFinished), I didn't get the call of this slot.
Is it an expected behavior of the signals/slots/moveToThread?
Problem is meta data information.
When you do a default connection between signal and slot and when signal is emitted from different thread than receiver is assigned to, Qt does a magic and wraps arguments for signal (creates a copy) and queue them in event loop of destination thread.
Then when destination tread executes logic of the Qt event loop, values are unwrapped and respective slot is invoked with copied values.
Now to be able to do this copy, Qt has to have some minimal knowledge about that type.
So when you use Qt types it will work out of the box, but you use external types like std::string in your case, you have to first perform type registration.
So basically your code is missing something like this:
// to be declared somewhere
Q_DECLARE_METATYPE(std::string);
// to be invoked at the beginning of program
qRegisterMetaType<std::string>();
Without type registration Qt doesn't know how to do a copy and provides a warning in logs. Check Qt logs I'm sure it prompts you with proper error message.

Qt C++ - How to pass data from a worker thread to main thread?

I am trying to perform interthread communication in Qt (C++). I have a worker thread which does some calculations and I want the workerthread to return its results to the main thread when done. I therefor use a connect, I know thanks to debugging, that the signal is successfully being emit but that it is the slot that isn t being executed and I don t understand why.
The relevant pieces of code:
webcamClass::webcamClass(QObject *parent) : QObject(parent)
{
workerThread = new QThread(this);
workerClassObj = new workerClass();
//connect for image
connect(workerClassObj, SIGNAL(mySignal(QPixmap)), this, SLOT(mySlot(QPixmap)));
//connect(&workerClassObj, workerClass::mySignal(QPixmap), this, webcamClass::mySlot(QPixmap));
connect( workerThread, SIGNAL(started()), workerClassObj, SLOT(getImage()) );
workerClassObj->moveToThread(workerThread);
}
void webcamClass:: foo()
{
workerThread->start();
}
void workerClass::getImage()
{
qint64 successFailWrite;
QImage img;
QPixmap pixmap;
... do some stuff with pixmap...
qDebug()<<"going to emit result";
emit mySignal(pixmap);
qDebug()<<"emitted";
}
void webcamClass::mySlot(QPixmap p)
{qDebug()<<"this message should be displayed"; }
The corresponding header files:
class workerClass : public QObject
{
Q_OBJECT
private:
public:
explicit workerClass(QObject *parent = nullptr);
signals:
void mySignal(QPixmap);
};
webcamClass::webcamClass(QObject *parent) : QObject(parent)
{
Q_OBJECT
public:
explicit webcamClass(QObject *parent = nullptr);
public slots:
void mySlot(QPixmap p);
private:
QThread *workerThread;
workerClass *workerClassObj;
};
The code above just outputs:
going to emit result
emitted
but unfortunately doesn t output this message should be displayed.
webcamClass belongs to the parent thread, while workerClass belngs to -you guessed it- the worker thread.
Could someone explain how to setup my connect so that mySlot() gets triggered?
Thanks!
In the code you pasted in pastebin.com/UpPfrNEt you have a getVideoFrame method that uses while (1). If this method is called, it runs all the time and blocks the event loop from handling signals. You can solve it in many ways, I think the best practice will be to replace the while(1) with something else.
If possible, I highly encourage you to use the new Signal Slot syntax:
connect( SOURCEINSTANCE, &CLASS::SIGNAL, TARGETINSTANCE, &CLASS::SLOT );
In your case, that could be:
connect( workerClassObj, &workerClass::mySignal, this, &webcamClass::mySlot );
Specificallyfor your case, if you want to pass Signals and Slots between threads, you have to be careful. First, check the connection type for the connect call, its acutally the last parameter.
connect( workerClassObj, &workerClass::mySignal, this, &webcamClass::mySlot, Qt::QueuedConnection );
For a detailed explanation look here:
http://doc.qt.io/qt-5/signalsandslots.html
If you want to pass custom types, you have to declare them as metatypes first.
Add e.G. this in your constructor:
qRegisterMetaType("MyDataType");
Please make sure, that your custom datatype has a default constructor and be aware that afaik, references cannot be passed across threads.

How to show images from an ip camera as a live video while also doing some audio processing in Qt?

In this application, I am using two audio devices, each of which can be used both as input and output devices. One audio device is used as an input and the other audio device uses the fed input sound as an output and same happens the other way around. While this audio processing goes on, I also try to show the image from an ip camera and update it to produce a live video.
A screenshot of the application:
As you can see, I can show the image, but not while doing any sort of audio processing. The problem is that whenever I try to do audio processing and show the video simultaneously, I run into problems as the program stops working altogether.
The slots I have are these:
private slots:
void on_startVideo_clicked();
void on_stopVideo_clicked();
void on_startInput1_clicked();
void on_stopInput1_clicked();
void on_startInput2_clicked();
void on_stopInput2_clicked();
void readMoreInput1();
void readMoreInput2();
Here all the slots, except on_startVideo_clicked() and on_stopVideo_clicked(), are relevant for audio processing and they work fine until I invoke the slots relevant for the video.
The slot for showing the video:
void MainWindow::on_startVideo_clicked()
{
const std::string streamUrl = "http://operator:operator#axis-p5515/axis-cgi/mjpg/video.cgi?resolution=1920x1080&compression=0&dummy=param.mjpg";
if (!capture.open(streamUrl))
{
QMessageBox::warning(this, "Error", "No input device availabe!");
}
else
{
videoState = true;
ui->stopVideo->setEnabled(true);
ui->startVideo->setEnabled(false);
while(videoState){
cv::Mat frame;
capture >> frame;
cv::cvtColor(frame, frame, CV_BGR2RGBA);
cv::resize(frame, frame, cv::Size(ui->liveVideo->width(), ui->liveVideo->height()), 0, 0);
QImage image(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGBA8888);
ui->liveVideo->setPixmap(QPixmap::fromImage(image));
QPixmap coloredImage(QPixmap::fromImage(image));
QPainter painter(&coloredImage);
painter.setOpacity(0.5);
painter.setBrush(Qt::red);
painter.drawRect(coloredImage.rect());
ui->overlayVideo->setPixmap(coloredImage);
qApp->processEvents(QEventLoop::AllEvents);
}
}
}
As you can see, I am using OpenCV for capturing the image and showing it. Obviously, I'm doing something wrong. Could you please point out where I'm doing wrong?
Thank you!
This sounds like a problem that could easily be solved with the C++ standard thread library. Try running the audio processing and OpenCV stuff in different concurrently-executing threads.
Whilst I agree with Mason Watmough's sentiment, you are using Qt and QThread abstracts a lot of the nasty stuff away and allows you to continue using signals and slots across the threads.
There is a good example on the QThread class page which i've added at the bottom of this post (to keep the top cleaner)
The layout of your program will then look something like this:
- ControllerClass
- VideoController
- VideoWorker
- AudioController
- AudioWorker
You can then use your ControllerClass to spawn off the threads, connect up to signals and slots, then use the outputs from the two concurrently running threads to do whatever behaviour you want.
You would do this in a way similar to this:
myThread = new VideoController();
connect(myThread, SIGNAL(depthValues(float, float, float)), this, SLOT(on_depth_values(float, float, float)));
QThread Example:
class Worker : public QObject
{
Q_OBJECT
QThread workerThread;
public slots:
void doWork(const QString &parameter) {
// ...
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};

Create Thread in QDialog and emit signals to QDialog in Qt

I created an application that has a mainwindow and from this window creates a QDialog. This QDialog should create a RenderThread that emits received images from the camera or in the example emits text. When i debug this it seems that the connection is never made as adding a breakpoint in the slot CameraWindow::debugSomething does not get caught. Whats wrong ?
I followed this example: http://qt-project.org/doc/qt-4.8/threads-mandelbrot.html but it seems that i've done something wrong.
qtDEVC::qtDEVC(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
connect (ui.pushButton_startCam, SIGNAL( clicked() ),this,SLOT( startCam() ) );
/**Threading*/
CameraWindow camWindow = new CameraWindow(this);
}
int qtDEVC::startCam()
{
camWindow.show();
camWindow.startCaptureThread();
}
CameraWindow Class:
CameraWindow::CameraWindow(QWidget *parent)
: QDialog(parent)
{
ui.setupUi(this);
connect(&thread, SIGNAL(sendText(std::string)),
this, SLOT(debugSomething(std::string)));
}
void CameraWindow::debugSomething(std::string something){
QString somethings(something.c_str());
qDebug()<<somethings;
}
int CameraWindow::startCaptureThread(){
RenderThread *thread = new RenderThread(this, guid, CLEYE_COLOR_RAW, CLEYE_VGA, 50);
thread->StartCapture(); //starts thread in low priority and sets _running to true
}
CameraWindow header
class CameraWindow : public QDialog
{
Q_OBJECT
public:
CameraWindow(QWidget *parent = 0);
~CameraWindow();
Ui::CameraWindow ui;
public slots:
int startCaptureThread();
void debugSomething(QString);
private:
RenderThread thread;
};
RenderThread Class
void RenderThread::run()
{
// image capturing loop
while(_running)
{
qDebug()<<("render while()"); //is printed with qdebug correctly
if (restart)
break;
if (abort)
return;
qDebug("it"); //is also printed with qdebug correctly
emit sendText(text);
}
RenderThread header
class RenderThread : public QThread
{
Q_OBJECT
public:
RenderThread(QObject *parent, GUID cameraGUID, CLEyeCameraColorMode mode, CLEyeCameraResolution resolution, float fps);
RenderThread();
~RenderThread();
bool StartCapture();
signals:
void sendText(QString &text);
protected:
void run();
private:
QMutex mutex;
QWaitCondition condition;
//some more private variables for construction
};
I think that this creation seems somehow wrong: RenderThread *thread = new RenderThread(this);
The first thing that's worrying about the question is the word "RenderThread". Note that Qt only allows rendering on the main thread. You can create separate threads for calculations of image data, but whenever you use a painter and draw objects, that must happen on the main thread. However, If you're just going to capture the image and pass that to the main thread, via signals and slots, then that should be ok.
Secondly, whilst you've not shown all your code, I'm assuming from the function called RenderThread::run() and from the Qt example that you may have inherited from QThread here. If this is the case, please note that this is not how to use QThread. Instead, you should have your class inherit from QObject and move that to a QThread. You can read about how to use QThread properly here.
I know it's a Qt example that you've followed, but even the guys at Qt think it's a bad idea. Here's an article of how to really use QThreads.
With that in mind, here's an outline of how I would use QThread for this: -
class CameraWindow : public QDialog
{
private:
CameraObject* m_pCamObject;
};
class CameraObject : public QObject
{
Q_OBJECT
public:
private slots:
startCaptureThread();
private:
};
int CameraWindow::startCaptureThread()
{
m_pCamObject = new CameraObject;
QThread* pThread = new QThread;
this->moveToThread(pThread); // CameraObject will run on the new thread
connect(pThread, SIGNAL(started()), m_pCamObject, SLOT(startCaptureThread()));
connect(pThread, SIGNAL(finished()), pThread, SLOT(deleteLater()); // clear up when
finished
pThread->start();
}
Note that here is a CameraObject, separated from the CameraWindow. The QThread is just a controller of the thread and keeps a clean separation from the CameraObject.
Another reason for handling threads this way is that you can move multiple QObject instances to the new thread, rather than always creating a new thread per object. If you've more threads than CPU cores, you're unlikely to gain by creating yet another thread.
In the constructor of CameraWindow class, you are connecting a RenderThread's signal but it is not the same object which is started in startCaptureThread. Change your startCaptureThread like this:
int CameraWindow::startCaptureThread()
{
thread.StartCapture(); //starts thread in low priority and sets _running to true
}
In this method, the thread member of CameraWindow is started.
P.S.: post the headers too, we can't see the members from this code.
In the mentioned example, CameraWindow class holds a RenderThread thread variable, not a RenderThread *thread.
You are connecting a pointer address in your connect call:
connect(&thread, SIGNAL(sendText(std::string)),
this, SLOT(debugSomething(std::string)));
Try to use a good address:
connect(thread, SIGNAL(sendText(std::string)),
this, SLOT(debugSomething(std::string)));
This is not a tested answer.