I have this very simple Qt code:
void thread_func()
{
int a1 = 1;
const char* a2[] = { "dummy_param" };
QApplication app(a1, (char**)a2);
QMessageBox msg(QMessageBox::NoIcon, "MyTitle", "Foo bar Foo bar", QMessageBox::Ok);
msg.exec();
}
If I call the above function from my main in a std::thread, it brings up the dialog:
int main()
{
std::thread t(thread_func);
t.join();
}
...but when I close it, I get the warning message:
QObject::~QObject: Timers cannot be stopped from another thread
I've checked that the thread affinity of both QApplication instance and msg is the same. Calling the thread_func function directly from my main() (without creating a std::thread) removes that message.
I am using Qt 5.15.1 on Windows 10.
What am I missing here? Thanks
It's not allowed to operate Qt GUI directly outside the main thread(GUI thead). You can emit signals.
The warning message says it all. Use a signal/slot mechanism to accomplish the same thing.
#include <QApplication>
#include <QMessageBox>
#include <QObject>
#include <QThread>
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(){}
public slots:
void displayMessageBox()
{
QMessageBox msg(QMessageBox::NoIcon, "MyTitle", "Foo bar Foo bar", QMessageBox::Ok);
msg.exec();
this->close();
}
};
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker() {}
void start() { emit askForMessageBox(); }
signals:
void askForMessageBox();
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget *widget;
Worker *worker;
QThread *thread(nullptr);
widget = new Widget();
worker = new Worker();
thread = new QThread(nullptr);
QObject::connect(worker, &Worker::askForMessageBox, widget, &Widget::displayMessageBox);
QObject::connect(thread, &QThread::started, worker, &Worker::start);
widget->show();
worker->moveToThread(thread);
thread->start();
return a.exec();
}
Related
Two.h
#ifndef TWO_H
#define TWO_H
#include <QObject>
#include <QThread>
#include <QDebug>
#include <QTimer>
class Two : public QObject
{
Q_OBJECT
private:
QTimer abc;
public:
QString m_xyz;
Two();
signals:
void emitThisSignal( int x, QString &y );
public slots:
void mySlot();
};
class Controller : public QObject
{
Q_OBJECT
private:
Two objTwo;
QThread objQThread;
Controller();
public slots:
void mySlot( int x, QString &y)
{
qDebug() << "\nWWWWWWWWWWWWW: " << y;
}
};
#endif // TWO_H
Two.cpp
#include "two.h"
Two::Two()
{
m_xyz = "aksja";
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &Two::mySlot);
timer->start(1000);
}
void Two::mySlot()
{
emit emitThisSignal(4, m_xyz);
qDebug()<< "FFFFFFFFFFF " << m_xyz;
}
Controller::Controller()
{
objTwo.moveToThread( &objQThread );
connect( &objTwo, &Two::emitThisSignal, this, &Controller::mySlot );
connect( &objQThread, &QThread::finished, &objQThread, &QThread::deleteLater );
objQThread.start();
}
Controller::~Controller()
{
delete objTwo;
objQThread.wait();
}
I can see that the signal is being emitted because of the print statement but the slot of the Controller class is not getting called.
void Two::mySlot()
{
emit emitThisSignal(4, m_xyz);
qDebug()<< "FFFFFFFFFFF " << m_xyz;
}
Why is that so?
int main( int argc, char* argv[])
{
QCoreApplication app(argc, argv);
Controller o;
return app.exec();
}
See documentation of QObject::connect, note last argument with default value: Qt::AutoConnection.
Its documentation says:
(Default) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.
Now you are fall in into Qt::QueuedConnection scenario:
The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
So basically you need something which will provide an event loop.
In this code you need that:
int main( int argc, char* argv[])
{
QCoreApplication app{argc, argv};
Controller o;
// note you need something what will stop this event loop to terminate application
return app.exec();
}
One more thing.
Now I noticed that your signals and slot argument is quite unusual. Problem might be second argument which type is QString&.
It might be source of problems I do not know if Qt is able to marshal non const references. If you will add const then it will be able to marshal QString and should work (if I didn't missed other pitfall).
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 would like to create a class that has its own QTimer and QThread for some projects for Robot's sensors. After some searching, this is what I came up with
#include <QCoreApplication>
#include <QTimer>
#include <QThread>
#include <QObject>
#include <QDebug>
//#####################( Robot Class )#########################3
class Robot : public QObject
{
public:
Robot(QObject *parent = 0);
~Robot();
private:
QTimer *mQTimer;
QThread *mQThread;
public slots:
void update();
};
Robot::Robot(QObject *parent)
: QObject(parent)
{
mQTimer = new QTimer(0);
mQThread = new QThread(this);
mQTimer->setInterval(1);
mQTimer->moveToThread(mQThread);
connect(mQTimer, SIGNAL(timeout()), this, SLOT(update()));
connect(mQThread, SIGNAL(started()), mQTimer, SLOT(start()));
mQThread->start();
//mQTimer->start();
}
Robot::~Robot()
{
delete mQTimer;
delete mQThread;
}
void Robot::update()
{
qDebug() << "Robot is updating ...";
}
//##################( Main )###########################
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Robot *myRobot = new Robot(0);
return a.exec();
}
I'm getting this error
QObject::connect: No such slot QObject::update() in ..\untitled1\main.cpp:34
QObject::connect: No such slot QObject::update() in ..\untitled1\main.cpp:34
You are missing the Q_OBJECT macro in your class also try to avoid naming the methods like that because you can mix it with Qt methods names. Also make additional header and cpp file for each class you create in this case make robtot.h and robot.cpp.
class Robot : public QObject
{
Q_OBJECT
public:
Robot(QObject *parent = 0);
~Robot();
...
The below works for me. You forgot the Q_OBJECT macro and to include the moc output that defines the static metadata for Robot.
There's of course no point to this code, since the Robot::update slot will execute in the main thread.
Your code has two threads: the main thread, where the Robot object lives, and the robot.mThread, where the timer lives. The timer will time out in the mThread, and will queue a slot call on the main thread. That slot call will end up invoking robot.update with a.exec on the stack.
Note that there's no need to explicitly have the timer and the thread allocated on the heap using new. They should be direct members of Robot, or of its PIMPL.
main.cpp
#include <QCoreApplication>
#include <QTimer>
#include <QThread>
#include <QDebug>
class Robot : public QObject
{
Q_OBJECT
QTimer mTimer;
QThread mThread;
public:
Robot(QObject *parent = 0) : QObject(parent) {
connect(&mTimer, &QTimer::timeout, this, &Robot::update);;
mTimer.start(1000);
mTimer.moveToThread(&mThread);
mThread.start();
}
Q_SLOT void update() {
qDebug() << "updating";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Robot robot;
return a.exec();
}
#include "main.moc"
It would make more sense to move the entire robot to its own thread. Note that when you do that, you need to set parentage on all objects owned by robot, so that they all switch threads along with the Robot object.
The Thread class fixes the long standing usability bug of QThread - it turns it into a truly RAII class that can be safely destructed at any time.
main.cpp
#include <QCoreApplication>
#include <QTimer>
#include <QThread>
#include <QDebug>
class Thread : public QThread {
using QThread::run;
public:
~Thread() { quit(); wait(); }
};
class Robot : public QObject
{
Q_OBJECT
QTimer mTimer;
int mCounter;
public:
Robot(QObject *parent = 0) : QObject(parent), mTimer(this), mCounter(0) {
connect(&mTimer, &QTimer::timeout, this, &Robot::update);;
mTimer.start(1000);
}
Q_SLOT void update() {
qDebug() << "updating";
if (++mCounter > 5) qApp->exit();
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Robot robot;
Thread thread;
robot.moveToThread(&thread);
thread.start();
return a.exec();
}
#include "main.moc"
Say, I have 2 threads: A and B, A is the main thread. In thread A, there are two on_button_click slots. The first one is:
on_button_one_clicked(){
myObject_one = new myObject(this);
myObject_one->setParent(map);
myObject_two = new myObject(this);
myObject_two->setParent(map);
...
}
The second one is:
on_button_two_clicked(){
foreach(myObject* i, map->childItems){
delete i;
}
}
Here, myObject and map are all QGraphicsItems. In thread B, a signal is emitted to trigger a slot of thread A:
slot_triggered_by_signal_from_thread_B(){
foreach(myObject* i, map->childItems){
i->do_some_thing();
}
}
Is this safe? Will it happen, when the codes comes to the line i->do_some_thing and meet an empty pointer and crash?
It is safe as long as you use the auto connection or queued conncetion type between the threads since then the slot will be only invoked when your other slot is finishing up or vice versa. They will not be running "simultaneously". That is the only concern that I could imagine for this not to be safe enough. I believe you meant a scenario like this:
main.cpp
#include <QThread>
#include <QApplication>
#include <QTimer>
#include <QObject>
#include <QPushButton>
#include <QDebug>
class Work : public QObject
{
Q_OBJECT
public:
explicit Work(QObject *parent = Q_NULLPTR) : QObject(parent) { QTimer::singleShot(200, this, SLOT(mySlot())); }
public slots:
void mySlot() { emit mySignal(); }
signals:
void mySignal();
};
class MyApplication : public QApplication
{
Q_OBJECT
public:
explicit MyApplication(int argc, char **argv)
: QApplication(argc, argv)
, pushButton(new QPushButton())
{
QStringList stringList{"foo", "bar", "baz"};
QThread *workerThread = new QThread();
Work *work = new Work();
work->moveToThread(workerThread);
connect(pushButton, &QPushButton::clicked, [&stringList] () {
for (int i = 0; i < stringList.size(); ++i)
stringList.removeAt(i);
});
connect(work, &Work::mySignal, [&stringList] () {
for (int i = 0; i < stringList.size(); ++i)
qDebug() << stringList.at(i);
});
}
~MyApplication()
{
delete pushButton;
}
QPushButton *pushButton;
};
#include "main.moc"
int main(int argc, char **argv)
{
MyApplication application(argc, argv);
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
QT += widgets
CONFIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Let's assume that main thread does some heavy work in on_button_two_clicked function.
Any other actions include user do something or another request from other thread
(in this case slot_triggered_by_signal_from_thread_B) will be blocked until
finishing on_button_two_clicked.
I think it means that it guarantees finishing previous event.
In conclusion, it is safe!
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();
}