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.
Related
I need some advice to access the field(QString name) variable in QWizardPage from a QThread. I'm building some kind of an installer and I want to do the installing work in a separate Thread.
My purpose:
When reached the commit/install page, I want to execute code to do the "installing" and update the QWizardPage with my progress, until its finished.
The install function is dependent on many field() variables from other QWizardPages. Therefore I tried to execute this install function from a QThread, which is defined in an inner class from my QWizardPage. The problem is, the field()-function i a non-static member and so it's not working. And so I'm out of ideas to run my install-function parallel to my WizardPage.
I tried something like this:
InstallPage.h
class InstallPage : public QWizardPage
{
Q_OBJECT
class WorkerThread : public QThread
{
Q_OBJECT
void run() override;
};
public:
InstallPage(QWidget *parent = 0);
private:
QLabel *lProgress;
WorkerThread *installer;
void install();
};
InstallPage.c
InstallPage::InstallPage(QWidget *parent)
: QWizardPage(parent)
{
...
installer = new WorkerThread(this);
installer->start();
}
void InstallPage::WorkerThread::run()
{
if(field("checkBox1").ToBool())
{
doStuff();
}
}
//QT-Creator says at field("checkBox1"):
//error: call to non-static member function without an object argument
I'm also open for any other idea to make my installer work. Maybe someone knows something I haven't thought of.
Another approach is to create a worker (QObject) that lives in another thread that performs the heavy task and notifies the status of that task through signals:
#include <QtWidgets>
class InitialPage: public QWizardPage
{
public:
InitialPage(QWidget *parent = nullptr): QWizardPage(parent)
{
QSpinBox *spinbox = new QSpinBox;
QLineEdit *lineedit = new QLineEdit;
QVBoxLayout *lay = new QVBoxLayout(this);
lay->addWidget(spinbox);
lay->addWidget(lineedit);
registerField("value1", spinbox);
registerField("value2", lineedit);
}
};
class InstallWorker: public QObject
{
Q_OBJECT
public:
InstallWorker(QObject *parent=nullptr): QObject(parent)
{
}
public Q_SLOTS:
void install(int param1, const QString & param2)
{
Q_EMIT started();
for(int i=0; i < 100; i++){
qDebug() << __PRETTY_FUNCTION__ << i << param1 << param2;
QThread::msleep(100);
Q_EMIT progressChanged(i);
}
qDebug()<< __PRETTY_FUNCTION__ << "finished";
Q_EMIT finished();
}
Q_SIGNALS:
void started();
void progressChanged(int value);
void finished();
};
class InstallPage: public QWizardPage
{
Q_OBJECT
public:
InstallPage(QWidget *parent = nullptr): QWizardPage(parent),
label(new QLabel), progressbar(new QProgressBar)
{
QVBoxLayout *lay = new QVBoxLayout(this);
lay->addWidget(label);
lay->addWidget(progressbar);
progressbar->setMinimum(0);
progressbar->setMaximum(100);
thread = new QThread(this);
worker.moveToThread(thread);
connect(&worker, &InstallWorker::started, this, &InstallPage::onStarted);
connect(&worker, &InstallWorker::finished, this, &InstallPage::onFinished);
connect(&worker, &InstallWorker::progressChanged, this, &InstallPage::onProgressChanged);
thread->start();
}
~InstallPage(){
thread->quit();
thread->wait();
}
void initializePage(){
start_install();
}
private Q_SLOTS:
void start_install(){
int param1 = field("value1").toInt();;
QString param2 = field("value2").toString();
QMetaObject::invokeMethod(&worker, "install", Qt::QueuedConnection, Q_ARG(int, param1), Q_ARG(QString, param2));
}
void onStarted(){
for(QWizard::WizardButton which: {QWizard::BackButton, QWizard::NextButton, QWizard::CancelButton})
if(QAbstractButton * button = wizard()->button(which))
button->setEnabled(false);
}
void onFinished(){
for(QWizard::WizardButton which: {QWizard::BackButton, QWizard::NextButton, QWizard::CancelButton})
if(QAbstractButton * button = wizard()->button(which))
button->setEnabled(true);
wizard()->next();
}
void onProgressChanged(int value){
progressbar->setValue(value);
label->setNum(value);
}
private:
InstallWorker worker;
QThread *thread;
QLabel *label;
QProgressBar *progressbar;
};
class FinalPage: public QWizardPage
{
public:
FinalPage(QWidget *parent = nullptr): QWizardPage(parent)
{
}
};
#include "main.moc"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWizard wizard;
wizard.addPage(new InitialPage);
wizard.addPage(new InstallPage);
wizard.addPage(new FinalPage);
wizard.show();
return app.exec();
}
I have two subclasses of QObject:
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
signals:
public slots:
void process();
};
And
class ThreadController : public QObject
{
Q_OBJECT
public:
explicit ThreadController(QObject *parent = 0);
public slots:
void sleep();
void wakeUp();
private:
QMutex *mutex;
QWaitCondition *wc;
};
void ThreadController::sleep(){
mutex->lock();
wc->wait(mutex);
mutex->unlock();
}
void ThreadController::wakeUp(){
mutex->lock();
wc->wakeOne();
mutex->unlock();
}
******************************************************
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
QThread* thread = new QThread;
Worker* worker = new Worker;
ThreadController*controller=new ThreadController();
controller->moveToThread(thread);
worker->moveToThread(thread);
QObject::connect(thread,&QThread::started,worker,&Worker::process);
QObject::connect(&w,&MainWindow::wakeClicked,controller,&ThreadController::wakeUp);
QObject::connect(&w,&MainWindow::sleepClicked,controller,&ThreadController::sleep);
thread->start();
return a.exec();
}
I move the worker and controller to the same QThread. I want to control the thread (put to sleep or wake up) by the ThreadController and the worker just doing its work, now the idea can not work, the worker can doing process but the controller can not work, I don't know what's wrong with my code.
Thanks #thuga yes,just as you say. Worker::process is a infinite loop like that:
void Worker::process(){
int a=(int)QThread::currentThreadId();
emit sendThreadId(a);
int b=0;
while (b<1000000) {
b++;
emit processResult(b);
QThread::msleep(1);// signal emit too quickly will block the main thread,
//so i just let the loop slow down
}
}
Why ThreadController::sleep is never executed if the process is infinite loop,but the ThreadController::wakeup can be executed i had tested.
QObject::connect(&w,&MainWindow::wakeClicked,controller,&ThreadController::wakeUp,Qt::DirectConnection);
yes the directconnection let it work.thanks!And i havs some questions:when a QThread uses QWaitCondition::wait() will stop the event loop in it? So any signal sends to the thread will not be processed anymore?
i want to reuse the QThread in my program.When the worker finishs its work will send a signal to the controller,and the controller will execute the sleep()function.Next time the data comes,controller wakeup() the thread and the worker goes to work again. Has any other solution to suit this case ?Reuse QThread or Delete it and new it(which Performance loss more)?
#thuga I need the signal&slot in the workerthread.But QtConcurrent::run could not provide such a mechanism.You should be correct that we don't have to put the thread to sleep when there is nothing to do. And i just finished the job with your help. Although my solution is not good,i posted all the code as shown below:
Worker Class for doing heavy jobs.
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
signals:
void processResult(int);
void sendThreadId(int);
public slots:
void process();
};
void Worker::process(){
QEventLoop eventloop;
int a=(int)QThread::currentThreadId();
emit sendThreadId(a);
int b=0;
while (b<1000000) {
b++;
emit processResult(b);
QThread::msleep(1);
eventloop.processEvents();
}
qDebug()<<"Worker::process() called";
qDebug()<<QThread::currentThreadId();
eventloop.exec();
}
ThreadController class for controling the worker QThread to sleep or wakeup.
class ThreadController : public QObject
{
Q_OBJECT
public:
explicit ThreadController(QObject *parent = 0);
~ThreadController();
signals:
void statusChanged(QString);
public slots:
void sleep();
void wakeUp();
private:
QMutex *mutex;
QWaitCondition *wc;
};
ThreadController::ThreadController(QObject *parent) : QObject(parent)
{
mutex=new QMutex();
wc=new QWaitCondition();
}
ThreadController::~ThreadController(){
delete mutex;
delete wc;
}
void ThreadController::sleep(){
qDebug()<<"ThreadController::sleep() called";
qDebug()<<QThread::currentThreadId();
emit statusChanged("WorkerThread sleeping");
mutex->lock();
wc->wait(mutex);
mutex->unlock();
}
void ThreadController::wakeUp(){
qDebug()<<"ThreadController::wakeUp() called";
qDebug()<<QThread::currentThreadId();
emit statusChanged("WorkerThread wakening");
mutex->lock();
wc->wakeAll();
mutex->unlock();
}
MainWindow Class for making a simple GUI(two QPushButtons and several QLabels)
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void wakeClicked();
void sleepClicked();
public slots:
void setMainId(int);
void setThreadId(int);
void setThreadStatus(QString);
void setThreadCount(int);
private:
QLabel*l_mainThreadId;
QLabel*l_workerThreadId;
QLabel*l_workerThreadStatus;
QLabel*l_workerThreadCount;
QString count;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setWindowTitle("Threading Test");
QWidget *window=new QWidget;
QVBoxLayout *mainLayout=new QVBoxLayout;
QHBoxLayout *mainLayout1=new QHBoxLayout;
QHBoxLayout *mainLayout2=new QHBoxLayout;
QLabel*label1=new QLabel("Main Thread ID:");
l_mainThreadId=new QLabel;
int a=(int)QThread::currentThreadId();
QString idlabel=QString::number(a);
l_mainThreadId->setText(idlabel);
mainLayout1->addWidget(label1);
mainLayout1->addWidget(l_mainThreadId);
QPushButton* wakeButton=new QPushButton("WakeUp");
QPushButton*sleepButton=new QPushButton("Sleep");
mainLayout2->addWidget(wakeButton);
mainLayout2->addWidget(sleepButton);
mainLayout->addLayout(mainLayout1);
mainLayout->addLayout(mainLayout2);
QVBoxLayout *threadLayout=new QVBoxLayout;
QHBoxLayout *threadLayout1=new QHBoxLayout;
QHBoxLayout *threadLayout2=new QHBoxLayout;
QHBoxLayout *threadLayout3=new QHBoxLayout;
QLabel *label2=new QLabel("Worker Thread ID:");
l_workerThreadId=new QLabel;
threadLayout1->addWidget(label2);
threadLayout1->addWidget(l_workerThreadId);
QLabel *label3=new QLabel("Worker Thread Status:");
l_workerThreadStatus=new QLabel;
threadLayout2->addWidget(label3);
threadLayout2->addWidget(l_workerThreadStatus);
QLabel *label4=new QLabel("Counting Result:");
l_workerThreadCount=new QLabel;
threadLayout3->addWidget(label4);
threadLayout3->addWidget(l_workerThreadCount);
threadLayout->addLayout(threadLayout1);
threadLayout->addLayout(threadLayout2);
threadLayout->addLayout(threadLayout3);
QHBoxLayout *layout=new QHBoxLayout;
layout->addLayout(mainLayout);
layout->addLayout(threadLayout);
window->setLayout(layout);
setCentralWidget(window);
setThreadStatus("WorkerThread wakening");
QObject::connect(wakeButton,&QPushButton::clicked,this,&MainWindow::wakeClicked);
QObject::connect(sleepButton,&QPushButton::clicked,this,&MainWindow::sleepClicked);
}
MainWindow::~MainWindow()
{
}
void MainWindow::setMainId(int id){
QString idlabel=QString::number(id);
l_mainThreadId->setText(idlabel);
}
void MainWindow::setThreadId(int id){
QString idlabel=QString::number(id);
l_workerThreadId->setText(idlabel);
}
void MainWindow::setThreadStatus(QString s){
l_workerThreadStatus->setText(s);
}
void MainWindow::setThreadCount(int i){
count = QString::number (i) ;
l_workerThreadCount->setText(count);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
QThread* thread = new QThread;
Worker* worker = new Worker;
ThreadController*controller=new ThreadController();
controller->moveToThread(thread);
worker->moveToThread(thread);
QObject::connect(thread,&QThread::started,worker,&Worker::process); QObject::connect(worker,&Worker::processResult,&w,&MainWindow::setThreadCount);
QObject::connect(worker,&Worker::sendThreadId,&w,&MainWindow::setThreadId);
QObject::connect(&w,&MainWindow::wakeClicked,controller,&ThreadController::wakeUp,Qt::DirectConnection);
QObject::connect(&w,&MainWindow::sleepClicked,controller,&ThreadController::sleep);
QObject::connect(controller,&ThreadController::statusChanged,&w,&MainWindow::setThreadStatus);
thread->start();
return a.exec();
}
The Program:
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"
I made a simple Qt project to cover the issue of calling Ui from another class.
The Files:
mainwindow.h | mainwindow.cpp | client.h | client.cpp | main.cpp
The Issue:
Connecting a signal from client.cpp to a slot in mainwindow.cpp worked very well.
But when I added a ui->statusBar->showMessage("message");
in the slot, it didn't work.
NOTE: When I made the signal and the slot both in mainwindow.cpp it worked, but calling a slot from mainwindow.cpp from a signal and connect() in client.cpp doesn't work.
The Codes: (trimmed to the essentials)
mainwindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
signals:
public slots:
void doSomething();
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
void MainWindow::doSomething()
{
QMessageBox::information(0, "test", "BINGO!!");
ui->statusBar->showMessage("testing");
}
client.h
class client : public QObject
{
Q_OBJECT
public:
explicit client(QObject *parent = 0);
void call();
signals:
void connected();
public slots:
};
client.cpp
client::client(QObject *parent) :
QObject(parent)
{
MainWindow main;
connect(this, SIGNAL(connected()), &main, SLOT(doSomething()));
call();
}
void client::call()
{
emit connected();
}
Added:
main.cpp
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Thanks.
client::client(QObject *parent) :
QObject(parent)
{
MainWindow main;
connect(this, SIGNAL(connected()), &main, SLOT(doSomething()));
call();
}
Your MainWindow lives on the stack. I think by the time your doSomething slot is triggered, the MainWindow object is already long gone. Try creating your MainWindow on the heap instead.
I see a major mistake in the code you have posted.
You have created an instance of MainWindow in the file main.cpp and thats the window you see when you run the application. But in your client.cpp you are creating a second instance of MainWindow and connecting the signal from the client object to the slot of the second MainWindow object you create. You are not seeing two main windows because you do not run the exec or show methods of this second instance.
Now coming to the solution, you will have to connect the signal from client to the proper MainWindow instance. I will suggest two methods below.
First:
I believe you create an instance of client in one of the member functions of the class MainWindow. If so then shift the connect method to the file mainwindow.cpp
Something like:
void MainWindow::someFunction()
{
client* c = new client();
connect(c, SIGNAL(connected()), this, SLOT(doSomething()));
c->call();
}
Second:
If you cannot do what is explained above, then you can use a combination of a static pointer and a static function to get the instance of MainWindow. Note that this method can also be used to ensure that there is only one instance of MainWindow at any given time.
mainwindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
signals:
public slots:
void doSomething();
public:
explicit MainWindow(QWidget* parent = 0);
~MainWindow();
static MainWindow* GetInstance(QWidget* parent = 0);
private:
Ui::MainWindow *ui;
static MainWindow* mainInstance;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow* MainWindow::mainInstance = 0;
MainWindow* MainWindow::GetInstance(QWidget *parent)
{
if(mainInstance == NULL)
{
mainInstance = new MainWindow(parent);
}
return mainInstance;
}
void MainWindow::doSomething()
{
QMessageBox::information(0, "test", "BINGO!!");
ui->statusBar->showMessage("testing");
}
Now in whatever class you need to get the instance of MainWindow in use, simply use the static function MainWindow::GetInstance to get the pointer to the MainWindow instance and use that as the parameter for connect.
i.e.,
MainWindow* instance = MainWindow::GetInstance();
client* c = new client();
connect(c, SIGNAL(connected()), instance , SLOT(doSomething()));
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.