I want to connect two threads. One Thread is the Mainthread of my application the other on is a workerthread. I have based my code on the following example doc.qt.io/qt-5/. For me it does not work completely. I am sending a QString to my WorkerThread (this works) and want to send it afterwards back to the MainThread (does not work). Before asking why I am doing this that's just a very simple example. The real Code is much more complex but I have exactly the same problem. If those example would run I am very sure that the complex one would work too. Here the code:
Main.cpp
#include "Controller_C.h"
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Controller_C Controller;
Controller.SendData("Hello World!");
return a.exec();
}
Controller_C.cpp
#include "Controller_C.h"
#include <qmessagebox.h>
Controller_C::Controller_C(QWidget *parent)
: QMainWindow(parent),
Worker(new Worker_C())
{
ui.setupUi(this);
Worker->moveToThread(&WorkerThread);
connect(&WorkerThread, SIGNAL(started()), this, SLOT(ThreadStarted()));
connect(this, SIGNAL(SendToWorker(QString)), Worker, SLOT(DoWork(QString)));
connect(Worker, SIGNAL(SendToController()), this, SLOT(ReceiveData()));
WorkerThread.start();
}
Controller_C::~Controller_C()
{
}
void Controller_C::SendData(QString aString)
{
QThread* Controller = QThread::currentThread();
QMessageBox::information(this, "Info", QString("We have send the following to the Worker Thread: %1").arg(aString));
emit SendToWorker(aString);
}
void Controller_C::ReceiveData(QString aString)
{
QThread* Controller = QThread::currentThread();
QMessageBox::information(this, "Info", QString("The Controller received the following: %1").arg(aString));
}
Controller_C.h
#ifndef CONTROLLER_C_H
#define CONTROLLER_C_H
#include <QtWidgets/QMainWindow>
#include "ui_Controller_C.h"
#include "Worker_C.h"
#include <qthread.h>
class Controller_C : public QMainWindow
{
Q_OBJECT
public:
Controller_C(QWidget *parent = 0);
~Controller_C();
void SendData(QString aString);
private:
Ui::Qt_TestEnvironmentClass ui;
Worker_C* Worker;
QThread WorkerThread;
signals:
void SendToWorker(QString);
public slots:
void ReceiveData(QString aString);
};
#endif // CONTROLLER_C_H
Worker_C.cpp
#include "Worker_C.h"
#include <qmessagebox.h>
#include <qthread.h>
Worker_C::Worker_C()
{
}
Worker_C::~Worker_C()
{
}
void Worker_C::DoWork(QString aString)
{
QThread* Worker = QThread::currentThread();
emit SendToController(aString);
}
Worker_C.h
#ifndef WORKER_C_H
#define WORKER_C_H
#include <QObject>
class Worker_C : public QObject
{
Q_OBJECT
public:
Worker_C();
~Worker_C();
public slots:
void DoWork(QString aString);
signals:
void SendToController(QString);
};
#endif // WORKER_C_H
Thanks for your help.
Like the way you definied the Slot/Signal in your header:
void ReceiveData(QString aString);
void SendToController(QString);
You should also use them like this in your connect:
connect(Worker, SIGNAL(SendToController(QString)), this, SLOT(ReceiveData(QString)));`
Related
In my App I have some problems with threads and GUI messages like QMessageBox or a new dialog. To reproduce I made a small app to show the problem:
mainwindow.cpp
#include <QDebug>
#include "mainwindow.h"
#include "ui_mainwindow.h"
void ThreadAddTree::run() {
//mClass->addTreeEx();
bool b = false;
emit addTree(&b);
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QString path = "";
mThreadAddTree = new ThreadAddTree(this, path);
connect(mThreadAddTree, SIGNAL(addTree(bool*)), this, SLOT(on_add_tree(bool*)), Qt::BlockingQueuedConnection);
//,Qt::DirectConnection
mThreadAddTree->start();
}
void MainWindow::on_add_tree(bool* newData) {
QMessageBox::information(this, tr("Information"),
tr("Button click!"));
*newData = true;
}
void MainWindow::addTreeEx()
{
QMessageBox::information(this, tr("Information"),
tr("Button click!"));
}
Mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtWidgets>
#include <QThread>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class ThreadAddTree : public QThread
{
Q_OBJECT
public:
ThreadAddTree(class MainWindow *nClass, const QString &path) {
mPath = path;
mClass = nClass;
}
signals:
void addTree(bool*);
protected:
void run();
QString mPath;
class MainWindow *mClass;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
friend class ThreadAddTree;
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void addTreeEx();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
protected:
ThreadAddTree *mThreadAddTree;
protected Q_SLOTS:
void on_add_tree(bool* newData);
};
#endif // MAINWINDOW_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
If I use in Thread the call: mClass->addTreeEx(); the app will crash, in case of non main GUI thread. Understood.
So I uncouple the call with emit a message emit addTree(&b); works well. Messagebox is shown and no crash.
But now it becomes complicated for me. I need to call mClass->addTreeEx(); in my app because it will do a couple of operations. The same function is also used outside an additional thread.
But in one case, the mClass->addTreeEx(); that is running inside the thread need to call the Messagebox.
So my question is here, how to manage, that I can emit the emit addTree(&b); from the function mClass->addTreeEx(); if it was called from the thread and the app will not crash in case of no GUI thread?
Interpreting the code author's intent (to degree) and open to be corrected.
The signal parameter of type bool* in void addTree(bool*); makes not much sense especially for the case with signal sender having the boolean variable on the stack. Either make it void addTree(bool) and send immediate value or just void addTree() and handle the boolean atomic flag on the slot side as std::atomic_bool thread_safe_flag; so checking on that flag will be as actual as possible if (thread_safe_flag). But such handling requires even more to ensure the signal delivered at the right time in sync with the value. This is irrelevant to Qt, though: Why do I need to acquire a lock to modify a shared "atomic" variable before notifying condition_variable and can be done either with or without Qt.
The problem of message box on UI thread preventing the other message box from appearing (this is again an interpretation of the author's problem with the code). We obviously need to have a handle operate the message box, say, dismiss it, in case if it is open already:
QMessageBox* m_msgBoxPtr{nullptr};
std::atomic_bool m_threadSafeFlag;
void UI_Class::mySlotToHandleMsgBox()
{
if (m_msgBoxPtr != nullptr) {
m_msgBoxPtr->close();
m_msgBoxPtr->deleteLater();
m_msgBoxPtr = nullptr;
mySlotToHandleMsgBox();
}
else {
m_msgBoxPtr = new QMessageBox(QMessageBox::Information, title, message);
m_msgBoxPtr->exec(); // assuming we want modal dialog as QMessageBox::information()
// otherwise do m_msgBoxPtr->show()
// if this is set on UI thread only then and no
// "waits" for it on other threads then it being atomic is enough;
// then don't bother with any sync "complications"
m_threadSafeFlag = true;
}
}
mainworker.h
#ifndef MAINWORKER_H
#define MAINWORKER_H
#include <QObject>
class MainWorker : public QObject
{
Q_OBJECT
signals:
void completed(void);
public slots:
void run(void);
};
#endif // MAINWORKER_H
mainworker.cpp
#include "mainworker.h"
#include <QThread>
void MainWorker::run(void)
{
QThread::sleep(1);
emit completed();
}
mainthread.h
#ifndef MAINTHREAD_H
#define MAINTHREAD_H
#include <QThread>
class MainThread : public QThread
{
Q_OBJECT
public:
MainThread(void);
};
#endif // MAINTHREAD_H
mainthread.cpp
#include "mainthread.h"
MainThread::MainThread(void)
: QThread(nullptr)
{
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(void);
signals:
void runJob(void);
public slots:
void jobCompleted(void);
private:
QPushButton m_runButton;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(void)
: QMainWindow(nullptr),
m_runButton(this)
{
connect(&m_runButton, SIGNAL(released()), this, SIGNAL(runJob()));
m_runButton.setText("RUN!");
setCentralWidget(&m_runButton);
}
void MainWindow::jobCompleted(void)
{
QMessageBox::information(this, tr("Info"), tr("Job completed!"));
}
main.cpp
#include <QApplication>
#include "mainwindow.h"
#include "mainthread.h"
#include "mainworker.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow window;
MainThread thread;
MainWorker worker;
worker.connect(&window, SIGNAL(runJob()), &worker, SLOT(run()));
window.connect(&worker, SIGNAL(completed()), &window, SLOT(jobCompleted()));
worker.moveToThread(&thread);
thread.start();
window.show();
int exitCode = a.exec();
thread.quit();
thread.wait();
return exitCode;
}
Of course you can add args to signals and slots and call jobCompleted() slot any time from GUI thread.
I am trying to send signal across threads.
For testing this situation, I wrote a test code.
I am working on ubuntu 16.04 machine and qt version is 4.8.
In my code, three class exists:
1 - timer_class -> in this class, I emit signal in timeout slot.
2 - test_worker -> I am using this class as thread's worker.
3 - main_class -> I create timer_class instance, and also create thread in this class constructor.
I am trying to connect timer_class signal to test_worker slot.
Here is my code:
First; timer_class :
Header File:
#ifndef TIMER_CLASS_H
#define TIMER_CLASS_H
#include <QTimer>
#include <QDebug>
#include <QObject>
class timer_class : public QObject
{
Q_OBJECT
public:
timer_class(QObject *parent = 0);
~timer_class();
void start_timer();
signals:
void dummy_signal();
private slots:
void on_timeout_occur();
private:
QTimer *timer;
};
#endif // TIMER_CLASS_H
Source File :
#include "timer_class.h"
timer_class::timer_class(QObject *parent)
:QObject(parent)
{
timer = new QTimer();
connect(timer, SIGNAL(timeout()), this, SLOT(on_timeout_occur()));
}
timer_class::~timer_class()
{
}
void timer_class::start_timer()
{
timer->start(1000);
}
void timer_class::on_timeout_occur()
{
qDebug() << "timeout occur";
emit dummy_signal();
}
Second, thread_worker class :
Header File :
#ifndef THREAD_WORKER_H
#define THREAD_WORKER_H
#include <QDebug>
#include <QObject>
class thread_worker : public QObject
{
Q_OBJECT
public:
thread_worker(QObject *parent = 0);
~thread_worker();
public slots:
void main_loop();
void on_dummy_signal();
};
#endif // THREAD_WORKER_H
Source File :
#include "thread_worker.h"
thread_worker::thread_worker(QObject *parent)
:QObject(parent)
{
}
thread_worker::~thread_worker()
{
}
void thread_worker::main_loop()
{
forever
{
//qDebug() << "In Main Loop";
}
}
void thread_worker::on_dummy_signal()
{
qDebug() << "dummy signal received";
}
And last, main_class :
Header file :
#ifndef MAIN_CLASS_H
#define MAIN_CLASS_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include "timer_class.h"
#include "thread_worker.h"
class main_class : public QObject
{
Q_OBJECT
public:
main_class(QObject *parent = 0);
~main_class();
private:
QThread *thread;
timer_class *tmr_class;
thread_worker *worker;
};
#endif // MAIN_CLASS_H
Source File:
#include "main_class.h"
main_class::main_class(QObject *parent)
:QObject(parent)
{
tmr_class = new timer_class();
worker = new thread_worker();
thread = new QThread();
worker->moveToThread(thread);
connect(tmr_class, SIGNAL(dummy_signal()), worker, SLOT(on_dummy_signal()));
connect(thread, SIGNAL(started()), worker, SLOT(main_loop()));
thread->start();
tmr_class->start_timer();
}
main_class::~main_class()
{
}
In main.cpp, I just create main_class instance like this :
#include <QCoreApplication>
#include "main_class.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
main_class *main_cl = new main_class();
qDebug() << "executing a.exec";
return a.exec();
}
When I run the application, I saw "timeout occur" in console, but I don't saw "dummy signal received". I can not find problem.
Could you help me about this problem ?
Thanks.
The basic problem is that you're creating a new QThread, moving your timer_class instance onto that thread and then invoking thread_worker::main_loop when the thread starts. Since thread_worker::main_loop is basically a busy-waiting loop...
void thread_worker::main_loop ()
{
forever
{
//qDebug() << "In Main Loop";
}
}
...the QThread never gets a chance to process events thus preventing any signals being received via queued connections.
The correct fix for all of this depends to a large extent on what work (if any) you want thread_worker::main_loop to do.
To get things going in the mean time simply remove the forever loop or comment out the line...
connect(thread, SIGNAL(started()), worker, SLOT(main_loop()));
Having done that you should see the "dummy signal received" messages.
I wanted to create a Qt widget which communicates with other classes on different threads via the signal / slot system. The recieving Objects are created in a Function wich is run via std::async.
The problem is: If the widget emits a signal the slot on the other thread is not called.
My Example:
I created the Class MainWindow which derives from QMainWindow and will live on the main thread. The class Reciever is created in a function which is called via std::async, and has a thread which should print something to the console.
I tested if the signal is emitted by connecting it to another slot on the same thread which works fine.
MainWindow.hpp
#pragma once
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
signals:
void send();
private slots:
void buttonClicked();
void recieve();
};
MainWindow.cpp
#include "MainWindow.hpp"
#include <iostream>
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QPushButton* start = new QPushButton("Start");
setCentralWidget(start);
start->show();
connect(start, SIGNAL(clicked(bool)), this, SLOT(buttonClicked()));
connect(this, SIGNAL(send()), this, SLOT(recieve()));
}
void MainWindow::buttonClicked()
{
std::cout << "MainWindow::buttonClicked()\n";
emit send();
}
void MainWindow::recieve()
{
std::cout << "MainWindow::recieve()\n";
}
Reciever.hpp
#include <QObject>
class Reciever : public QObject
{
Q_OBJECT
public:
Reciever(QObject *parent = 0);
public slots:
void recieve();
};
Reciever.cpp
#include "Reciever.hpp"
#include <iostream>
Reciever::Reciever(QObject *parent) : QObject(parent)
{
std::cout << "Reciever()" << std::endl;
}
void Reciever::recieve()
{
std::cout << "Reciever::recieve()" << std::endl;
}
main.cpp
#include "MainWindow.hpp"
#include "Reciever.hpp"
#include <QApplication>
#include <future>
void StartAndConnect(MainWindow &widget)
{
Reciever* rec = new Reciever();
QObject::connect(&widget, SIGNAL(send()), rec, SLOT(recieve()));
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow myWidget;
myWidget.show();
auto future = std::async(std::launch::async, [&myWidget](){
StartAndConnect(myWidget);
});
app.exec();
future.wait();
}
After some research my strongest guess was, that the thread launched by std::async does not has a Qt event-loop and thus will not come to a point where the posted event (emit) is processed. I changed the main to use QtConcurrent::run but it also did not work.
EDIT
Here my try with QtConcurrent:
main2.cpp
#include "MainWindow.hpp"
#include "Reciever.hpp"
#include <QApplication>
#include <future>
#include <QtConcurrent>
void StartAndConnect(MainWindow &widget)
{
Reciever* rec = new Reciever();
QObject::connect(&widget, SIGNAL(send()), rec, SLOT(recieve()));
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow myWidget;
myWidget.show();
auto future = QtConcurrent::run( [&myWidget](){
StartAndConnect(myWidget);
});
app.exec();
future.waitForFinished();
}
You need a running event loop in your thread, if you want to process cross-thread slot calls.
auto future = QtConcurrent::run( [&myWidget](){
StartAndConnect(myWidget);
QEventLoop e;
e.exec();
});
But I recomend to use QThread, because in your case it is obvious. Qt has a very good documentation, that describes your case.
I applied an C++ example of working with threads in Qt 5.7. All things are good except two things:
1- I used Signal & Slot to update my label in the main form. the problem is that is no effect. Really, I don't know where is the issue.
2- The loop works fine, but when I exit my program I see (throught the "Application Output") that the loop still work (I think that's related with the started thread).
This my little example:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
void updateLabelText(const QString TheString);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "worker.h"
//#include <QDebug>
#include <QThread>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QThread *workerThread = new QThread;
Worker *worker = new Worker;
worker->moveToThread(workerThread);
connect(worker, SIGNAL(sendText(const QString)), this, SLOT(updateLabelText(QString)));
workerThread->start();
}
void MainWindow::updateLabelText(const QString TheString)
{
ui->label->setText(TheString);
}
worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker();
public slots:
void doWork();
signals:
void sendText(const QString);
};
#endif // WORKER_H
worker.cpp
#include "worker.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QThread>
#include <QMessageBox>
#include <QApplication>
Worker::Worker()
{
doWork();
}
void Worker::doWork()
{
for (int i = 0; i<999999; i++) {
emit sendText(QString::number(i));
qDebug() << "The number is : " + QString::number(i);
qApp->processEvents();
//QThread::msleep(5);
}
}
How can I fix this?
Thanks.
In your code, doWork() function is called from the Worker's constructor, the constructor is invoked in the main thread (that is done in the line Worker* worker= new Worker;).
Of course, that is not what you meant to do, since it will cause the main thread to execute the for loop in doWork() before even reaching into the connect call.
Instead of calling doWork() from the Worker's constructor, you should connect the thread's started() signal to doWork() slot, then call thread->start() after moving the Worker object to the new thread. This will leverage Qt cross-thread signals to invoke doWork() in the new thread as soon as it starts.
Here is how your code should look like:
#include <QtWidgets>
//QThread wrapper for safe destruction
//see http://stackoverflow.com/a/19666329
class Thread : public QThread{
using QThread::run; //final
public:
Thread(QObject* parent= nullptr): QThread(parent){}
~Thread(){ quit(); wait();}
};
class Worker : public QObject{
Q_OBJECT
public:
explicit Worker(QObject* parent= nullptr): QObject(parent){}
~Worker()= default;
Q_SIGNAL void sendText(QString text);
Q_SIGNAL void workFinished();
Q_SLOT void doWork(){
for (int i = 0; i<1000; i++) {
emit sendText(QString::number(i));
QThread::msleep(5);
}
emit workFinished();
}
};
class Widget : public QWidget{
Q_OBJECT
public:
explicit Widget(QWidget* parent= nullptr): QWidget(parent){
layout.addWidget(&buttonWork);
layout.addWidget(&label);
connect(&buttonWork, &QPushButton::clicked,
this, &Widget::buttonWorkClicked);
}
~Widget()= default;
Q_SLOT void buttonWorkClicked(){
Thread* thread= new Thread(this);
Worker* worker= new Worker;
worker->moveToThread(thread);
//invoke doWork as soon as the thread is started
connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::sendText, this, &Widget::updateLabelText);
//quit the thread when work is finished
connect(worker, &Worker::workFinished, thread, &QThread::quit);
//destroy thread and worker object when work is finished
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
//start the thread
thread->start();
}
Q_SLOT void updateLabelText(QString text){
label.setText(text);
}
private:
QVBoxLayout layout{this};
QPushButton buttonWork{"Work"};
QLabel label{"No work yet"};
};
int main(int argc, char* argv[]){
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"
I solved my problem by following the comment of Mr #Mike.
Simply, I removed the doWork() function from the instructor. Then, I added new connection between the thread's started() SIGNAL and worker's doWork() SLOT.
So thes are the changes:
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "worker.h"
#include <QThread>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
QThread *workerThread = new QThread;
Worker *worker = new Worker;
connect(workerThread, SIGNAL(started()), worker, SLOT(doWork()),Qt::QueuedConnection);
connect(worker, SIGNAL(sendText(const QString)), this, SLOT(updateLabelText(const QString)),Qt::QueuedConnection);
worker->moveToThread(workerThread);
workerThread->start();
}
void MainWindow::updateLabelText(const QString TheString)
{
ui->label->setText(TheString);
}
worker.cpp
#include "worker.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QThread>
#include <QMessageBox>
#include <QApplication>
Worker::Worker()
{
doWork();
}
void Worker::doWork()
{
for (int i = 0; i<999999; i++) {
emit sendText(QString::number(i));
qDebug() << "The number is : " + QString::number(i);
qApp->processEvents();
QThread::msleep(5);
}
}
Okay, so I've completely lost in QTimer. The problem is: I have multithreaded application, and I need to do some work on QTimer's timeout. I've done like this:
QTimer* timer = new QTimer();
timer->setSingleShot(true);
connect(timer, SIGNAL(timeout()), someObject, SLOT(work()));
And this did not work. Sometimes, work() was not called at all, sometimes it was called when I closed program, and sometimes all seemed normal.
So I've come to idea that timer needs thread. To provide MCV example :
class Tester : public QObject
{
Q_OBJECT
public:
Tester(QObject* par = 0) : QObject(par)
{
}
public slots:
void greet()
{
qDebug()<<"hello";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer* timer1 = new QTimer();
QThread* thread = new QThread();
Tester* tester = new Tester();
timer1->setInterval(500);
timer1->setSingleShot(false);
timer1->moveToThread(thread);
QObject::connect(thread, SIGNAL(started()), timer1, SLOT(start()));
QObject::connect(timer1, SIGNAL(timeout()), tester, SLOT(greet()));
QObject::connect(timer1, SIGNAL(timeout()), timer1, SLOT(deleteLater()));
QObject::connect(timer1, SIGNAL(destroyed()), thread, SLOT(quit()));
thread->start();
thread->wait();
delete thread;
delete tester;
return a.exec();
}
And this example does nothing. It does not greet me, so timeout is not called, and it does not end, so thread is not stopped. So questions are:
1. What is wrong with this code?
2. How properly use QTimer in multithreaded environment?
Just calling QTimer::setInterval doesn't start the QTimer. It just sets the interval in which the QTimer::timeout signal is emitted. You didn't start the QTimer. Use QTimer::start.
I think you're doing several mistakes here. You need to move the timer to thread after all the connections are made. Not sure how safe would be to start the timer when the thread starts because at that point, the timer would be in one thread and the thread object would be in another thread. QTimer must be started from the same thread in which it was created. The QThread object is just a thread handler and it lives in the thread in which was created. See the modifications I made to your example:
#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QTimer>
#include <QObject>
//#include "tester.h"
class Tester : public QObject
{
Q_OBJECT
public:
Tester(QObject* par = 0) : QObject(par)
{
}
public slots:
void greet()
{
qDebug()<<"hello";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer* timer1 = new QTimer();
QThread* thread = new QThread();
Tester* tester = new Tester();
timer1->setInterval(500);
timer1->setSingleShot(false);
timer1->start();
QObject::connect(timer1, SIGNAL(timeout()), tester, SLOT(greet()));
timer1->moveToThread(thread);
thread->start();
return a.exec();
}
It's not the safest/best fix(memory leaks and other) but it is, i hope, an example on which you can build.
This would be in my opinion the right and without memory leaks way to use a QTimer in another QThread:
handler.h
#ifndef HANDLER_H
#define HANDLER_H
#include <QObject>
class QTimer;
class QThread;
class Tester;
class Handler : public QObject
{
Q_OBJECT
public:
explicit Handler(QObject *parent = 0);
~Handler();
void exec();
private:
QTimer* timer;
QThread* thread;
Tester* tester;
};
#endif // HANDLER_H
handler.cpp
#include "handler.h"
#include "tester.h"
#include <QThread>
#include <QDebug>
#include <QTimer>
#include <QObject>
#include <QCoreApplication>
Handler::Handler(QObject *parent) :
QObject(parent)
{
timer = new QTimer;
thread = new QThread(this);
tester = new Tester(this);
timer->setInterval(500);
timer->setSingleShot(false);
QObject::connect(timer, SIGNAL(timeout()), tester, SLOT(greet()));
QObject::connect(thread, SIGNAL(destroyed()), timer, SLOT(deleteLater()));
}
Handler::~Handler()
{
thread->wait();
}
void Handler::exec()
{
timer->start();
timer->moveToThread(thread);
thread->start();
}
tester.h
#ifndef TESTER_H
#define TESTER_H
#include <QObject>
class Tester : public QObject
{
Q_OBJECT
public:
Tester(QObject* par = 0);
public slots:
void greet();
};
#endif // TESTER_H
tester.cpp
#include "tester.h"
#include <QDebug>
Tester::Tester(QObject *parent) :
QObject(parent)
{
}
void Tester::greet()
{
qDebug()<<"hello";
}
main.cpp
#include <QCoreApplication>
#include "handler.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Handler handler;
handler.exec();
return a.exec();
}