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:
Related
It was my first attempt to create a thread in qt but unfortunately it isn't stopping. This is how my code look like.
// Worker class
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
forever{
if(QThread::currentThread()->isInterruptionRequested()){
printf("true");
return;
}else{
if(!QThread::currentThread()->isInterruptionRequested()){
cout<<parameter.toStdString();
}
}
}
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
// Controller class
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller(QObject *parent = nullptr):QObject(parent) {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void stop(){
workerThread.requestInterruption();
}
signals:
void operate(const QString &);
};
The forever loop isn't stopping when stop function is called.Have I done anything wrong?
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 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 have two classes:A and B,both of them subclass from the QObject.
class A:public QObject
{
Q_OBJECT
public:
A();
~A();
}
i just referred to this article.
Then in the GUI (say the main Thread)I try to exchange data between A and B in different threads using
pA = new A();
QThread *workerThread = new QThread;
pA->moveToThread(workerThread);
connect(workerThread , SIGNAL(started()), pA, SLOT(doWork()));
connect(pA, SIGNAL(finished()), workerThread , SLOT(quit()));
connect(pA, SIGNAL(finished()), pA, SLOT(deleteLater()));
connect(workerThread , SIGNAL(finished()), workerThread , SLOT(deleteLater()));
workerThread ->start();
pB = new B();
connect(pA,SIGNAL(sigProduce(double)),pB,SLOT(slotConsume(double)));//I just don't know where to put this line
QThread *workerThread = new QThread;
pB ->moveToThread(workerThread);
connect(workerThread , SIGNAL(started()), pB , SLOT(doWork()));
connect(pB , SIGNAL(finished()), workerThread , SLOT(quit()));
connect(pB , SIGNAL(finished()), pB , SLOT(deleteLater()));
connect(workerThread , SIGNAL(finished()), workerThread , SLOT(deleteLater()));
workerThread ->start();
sigProduce(double) is a signal defined in the A object and slotConsume(double) is a public slot:
header of class B:
public slots:
void slotConsume(double);
so after the program started obj B can not get the double data from obj A.Does that connect(pA,pB) code's position matters?can anyone help me?THANKS.
Before I go into the details how to solve your problem, make sure the problem is valid in the first place. If the only reason you're using threads is because you want an infinite loop, then maybe you don't need threads at all. If the work inside the loop is light, then you can just do it in the main thread with a QTimer. Read this article to know more about this.
Anyway the problem you have is because you have an infinite loop in your doWork method, which is blocking the thread's event loop. Because of this you had to resort to calling processEvents. However this is not a good design. A better way would be to let the thread go back to the event loop by removing the infinite loop from doWork.
You can then simulate a loop with a timer, and stop the timer when you want to stop the thread. Or you can use QMetaObject::invokeMethod with a Qt::QueuedConnection to call the doWork method from the event loop, and use a bool condition top stop the work. In this example I used the invokeMethod option:
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include <QDebug>
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QObject *parent = 0) : QObject(parent), _stop(false) {}
public slots:
void doWork()
{
if(_stop)
return;
QThread::sleep(1); // simulating some heavy work here
emit calculationComplete(1564);
QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection);
}
void stop()
{
_stop = true;
}
signals:
void calculationComplete(int result);
private:
bool _stop;
};
class ResultHandler : public QObject
{
Q_OBJECT
public:
ResultHandler(QObject *parent = 0) : QObject(parent) {}
public slots:
void handleResult(int result)
{
// do something with result
qDebug() << Q_FUNC_INFO << "Result:" << result;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread thread_a;
Worker worker;
ResultHandler handler;
QObject::connect(&worker, &Worker::calculationComplete, &handler, &ResultHandler::handleResult);
QObject::connect(&thread_a, &QThread::started, &worker, &Worker::doWork);
QObject::connect(&thread_a, &QThread::finished, &worker, &Worker::stop);
worker.moveToThread(&thread_a);
thread_a.start();
QTimer::singleShot(5000, &thread_a, &QThread::quit); // just simulating an exit
int result = a.exec();
thread_a.quit();
thread_a.wait(5000);
return result;
}
#include "main.moc"
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.