main.cpp:
#include <QCoreApplication>
#include <QtCore>
#include "myobject.h"
QThread* cThread;
MyObject* cObject;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
cThread = new QThread();
cObject = new MyObject();
cObject->moveToThread(cThread);
QObject::connect(cThread, SIGNAL(started()),
cObject, SLOT(doWork()));
QObject::connect(cThread, SIGNAL(finished()),
cThread, SLOT(deleteLater()));
QObject::connect(cThread, SIGNAL(finished()),
cObject, SLOT(deleteLater()));
cThread->start();
return a.exec();
}
myobject.cpp:
#include "myobject.h"
MyObject::MyObject(QObject *parent) :
QObject(parent)
{
}
void MyObject::doWork()
{
qDebug() << "Hi";
QThread::currentThread()->quit();
return;
}
myobject.h:
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QtCore>
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = 0);
signals:
public slots:
void doWork();
};
#endif // MYOBJECT_H
Apparently, according to: https://stackoverflow.com/a/16062717, there is a memory leak, but how do I fix it? I guess I would have to return to the event loop and then call quit? But the issue is that I don't have access to the event loop.
There isn't a memory leak. Qt does clean up properly if you stick to its object model, and object trees and ownership. I also like following the documented examples.
Here is the example you referenced, with observation added on deleteLater().
main.cpp
#include <QCoreApplication>
#include <QtCore>
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public slots:
void deleteLater()
{
qDebug() << Q_FUNC_INFO;
QThread::deleteLater();
}
};
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = 0){}
signals:
public slots:
void deleteLater()
{
qDebug() << Q_FUNC_INFO;
QObject::deleteLater();
}
void doWork()
{
qDebug() << "Hi";
QThread::currentThread()->quit(); // It is supposed to stop here, but it doesn't.
return;
for (int i = 0; i < 1000000; i++) {
qDebug() << i;
}
}
};
QThread* cThread;
MyObject* cObject;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
cThread = new MyThread();
cObject = new MyObject();
cObject->moveToThread(cThread);
QObject::connect(cThread, SIGNAL(started()),
cObject, SLOT(doWork()));
QObject::connect(cThread, SIGNAL(finished()),
cThread, SLOT(deleteLater()));
QObject::connect(cThread, SIGNAL(finished()),
cObject, SLOT(deleteLater()));
cThread->start();
return a.exec();
}
output:
Hi
void __thiscall MyObject::deleteLater(void)
void __thiscall MyThread::deleteLater(void)
Hope that helps.
I am the poster on the link. There is in fact no memory leak with the default connection. By subclassing deleteLater and destructors like #phyatt did you obtain :
Hi
void MyObject::deleteLater()
virtual MyObject::~MyObject() Being deleted
void MyThread::deleteLater()
virtual MyThread::~MyThread() Being deleted
But If you use Qt::QueueConnection for instance in your connections you obtain:
Hi
void MyThread::deleteLater()
virtual MyThread::~MyThread() Being deleted
And the object cObject is leaked.
It is undocumented when the thread will effectively exit. So I cannot argue whether this behavior will always be the same. One possibility is to make the thread launcher responsible for doing the cleanup work. For instance:
void cleanup(){
cThread->exit();
cThread->wait();
delete cThread;
delete cObject;
}
To wrap things up, you don't have to fix anything with this code.
Related
I want to update UI from the second thread, I have created slots which are going to be called from other thread by signaling, but somehow it is not being called from the other thread. Below is the code:
WorkerThread.h
class WorkerThread: public QObject
{
Q_OBJECT
public:
WorkerThread();
~WorkerThread();
public slots:
void onStart();
signals:
void sendMessage(const QString& msg, const int& code);
};
WorkerThread.cpp
#include "workerwhread.h"
WorkerThread::WorkerThread(){}
void WorkerThread::onStart(){
emit sendMessage("start", 100);
}
Usage:
MyWidget.h
namespace Ui {
class MyWidget;
}
class MyWidget: public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
private slots:
void onGetMessage(const QString &msg, const int& code);
private:
Ui::MyWidget *ui;
QThread *thread = nullptr;
WorkerThread *wt = nullptr;
};
MyWidget.cpp
MyWidget::MyWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MyWidget)
{
ui->setupUi(this);
wt = new WorkerThread;
thread = new QThread;
connect(thread, &QThread::finished, wt, &QObject::deleteLater);
connect(ui->btStart, &QPushButton::clicked, wt, &WorkerThread::onStart);
connect(wt, &WorkerThread::sendMessage, this, &MyWidget::onGetMessage);
wt->moveToThread(thread);
thread->start();
}
void MyWidget::onGetMessage(const QString &msg, const int& code)
{
qDebug() << "message" << msg; // this never being called ????
}
Note: When I pass the connection type Qt::DirectConnectoin, then it is working, but the problem is it is not the GUI thread.
connect(wt, &WorkerThread::sendMessage, this, &MyWidget::onGetMessage, Qt::DirectConnection);
Main
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
w.setWindowIcon(QIcon(":/icons/system.png"));
return a.exec();
}
After a lot of trying and checking the code line by line, I finally found the problem. The reason was with overriding the event() function of QWidget, the return value of event() function is bool, so if you return a true it is OK and working well without throwing any runtime or compile-time error. But it will prevent signal-slot events to happen.
So NOT return true, but return QWidget::event(event); then it will slove the problem.
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);
}
}
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)));`
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"
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();
}