I cannot produce a very simple example to getting start with Qt multi-thread. I read a lot of posts and tutorials but still it doesn't work.
Goal
Have a background worker independent from the GUI. Oh, wow...
What I did
A simple example:
create the Engine class
that shows a QMainWindow
and starts a QTimer that prints a number
But if you left-click the title bar of the GUI, keeping pressed the mouse button (i.e. on the minimize button) the counter will stop! Even if it was created in a non-GUI environment and it was moved in another thread!
Why?
main.cpp
#include "engine.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Engine e;
return a.exec();
}
engine.h
#ifndef ENGINE_H
#define ENGINE_H
#include <QObject>
#include <QThread>
#include <QTimer>
#include "mainwindow.h"
class Engine : public QObject
{
Q_OBJECT
public:
explicit Engine(QObject *parent = 0);
private:
MainWindow mainWindow;
QThread *thread;
QTimer *timer;
private slots:
void foo();
};
#endif // ENGINE_H
engine.c
#include "engine.h"
#include <QDebug>
Engine::Engine(QObject *parent) : QObject(parent)
{
thread = new QThread(this);
timer = new QTimer();
timer->setInterval(100);
connect(timer, &QTimer::timeout, this, &Engine::foo);
connect(thread, &QThread::started, timer, static_cast<void (QTimer::*)(void)>(&QTimer::start));
timer->moveToThread(thread);
thread->start();
mainWindow.show();
}
void Engine::foo()
{
static int i;
qDebug() << ++i;
}
The QMainWindow contains no code.
Basically, Qt has one thread dealing with the GUI (typically the main thread). Any objects specific to this thread will be blocked by GUI work. You need to keep the GUI outside of your interacting partners.
To be more specific, your Engine object resides in the GUI/main thread. Even while your timer is sent to a worker thread, its signals are dispatched to the slot foo() in the main thread.
You need to de-mangle Engine and the main window such that Engine can reside in its own thread and process signals while the GUI is blocking.
Looks like you moved QTimer instance to your custom thread, but you didnt move Engine instance to this thread, therefore, foo slot of Engine class will be executed in main thread.
I can suggest you using additional helping QObject-derived class instance within Engine class and move it to your new thread in Engine constructor.
With following changes solution works fine even with minimize button pressed (I've commented places where i added or changed anything. Also added new QObject-derived class EngineWorker):
Engine.h
#ifndef ENGINE_H
#define ENGINE_H
#include <QObject>
#include <QThread>
#include <QTimer>
#include "mainwindow.h"
#include "engineworker.h"
class Engine : public QObject
{
Q_OBJECT
public:
explicit Engine(QObject *parent = 0);
private:
MainWindow mainWindow;
QThread *thread;
QTimer *timer;
//additional QObject-derived class
EngineWorker *worker;
private slots:
void foo();
};
#endif // ENGINE_H
Engine.cpp
#include "engine.h"
#include <QDebug>
Engine::Engine(QObject *parent) : QObject(parent)
{
thread = new QThread(this);
timer = new QTimer();
//Creating instance of Engine worker
worker = new EngineWorker();
timer->setInterval(100);
//Connecting Engine worker' foo slot to timer
connect(timer, &QTimer::timeout, worker, &EngineWorker::foo);
connect(thread, &QThread::started, timer, static_cast<void (QTimer::*)(void)>(&QTimer::start));
timer->moveToThread(thread);
//Moving worker to custom thread
worker->moveToThread(thread);
thread->start();
mainWindow.show();
}
void Engine::foo()
{
static int i;
qDebug() << ++i;
}
EngineWorker.h
#ifndef ENGINEWORKER_H
#define ENGINEWORKER_H
#include <QObject>
#include <QDebug>
class EngineWorker : public QObject
{
Q_OBJECT
public:
explicit EngineWorker(QObject *parent = 0);
signals:
public slots:
void foo();
};
#endif // ENGINEWORKER_H
EngineWorker.cpp
#include "engineworker.h"
EngineWorker::EngineWorker(QObject *parent) : QObject(parent)
{
}
//foo slot of EngineWorker class
void EngineWorker::foo()
{
static int j;
qDebug() <<"Worker: "<< ++j;
}
Related
Hello Im trying to print my result from a thread to textBrower in qtwidget QT but I cant, either I get error or the program wont compile
is there another way ?? how can I change the textBrowser outside of the class's function??
PS BTW I need to keep looping inside the thread cuz actually im getting some data from uart in final program (in this code i just wanna print "lol" which eventually I wanna change it with some other code which take data ) so I cant come out of the loop
eventually i want use some process from another library and show the resault REAL TIME
bool stop;
out::out(QWidget *parent) :
QWidget(parent),
ui(new Ui::out)
{
ui->setupUi(this);
ui->textBrowser->setText("lol");
}
out::~out()
{
delete ui;
}
///////////////////////////////////////////////
class t : public out, public QThread
{
public:
void run()
{
while(1){
qDebug()<<"hi"<<i;
// ui->textBrowser->setText("lol"); I tried to use this but it didnt worked
if(stop==true){
QThread::exec();
}
}
}
};
void mamad1(void){ //this function get called from another cpp file and its for starting the thread
stop=false;
t *b = new t;
b->start();
}
void out::on_pushButton_clicked() // a push button to stop the thread
{
stop=true;
}
I tried make ui in out.h file a public property and use ui->textBrowser->setText("lol"); but it didn't worked the program freezed and i got this
error : (Parent is QTextDocument(0x208d812a510), parent's thread is QThread(0x208d6816de0), current thread is QThread(0x208dac94e10)
I tried to use connect() also and didn't worked or I didn't use it right
The concept of QT the GUI thread is called the master thread. This thread has an event queue. In general, this event queue is populated by internal and external events. Moreover, the queue between threads is an efficient approach for thread communication. The same is true for the worker threads as well. When you call the start method through the worker threads their signal queue is created and ready to be consumed by the corresponding thread.
Let's come back to your question. The solution is simple. From your worker threads, you can just send a signal to the master thread. The master thread will see this event that modifies the GUI item and forwards this signal/event to the slot that is responsible to take action accordingly. Just define a signal in your worker thread and connect your master thread to this signal with a given slot.
UPDATE FOR SOLUTION
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <MyWorkerThread.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void setWorkerThread(MyWorkerThread* thread);
void connectToSignals();
public slots:
void handleTextBoxSignal(const QString& text);
private:
Ui::MainWindow *ui;
MyWorkerThread* m_worker_thread{nullptr};
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
void MainWindow::setWorkerThread(MyWorkerThread* thread)
{
m_worker_thread = thread;
}
void MainWindow::connectToSignals()
{
QObject::connect(m_worker_thread,
SIGNAL(changeTextOnUI(QString)),
this,
SLOT(handleTextBoxSignal(QString)));
}
void MainWindow::handleTextBoxSignal(const QString& text)
{
qDebug() << "Text box change signal received with text: " << text;
auto text_box = findChild<QTextEdit*>("myTxtEdit");
if(text_box != nullptr)
{
text_box->setText(text);
}
}
MainWindow::~MainWindow()
{
delete ui;
}
MyWorkerThread.h
#ifndef MYWORKERTHREAD_H
#define MYWORKERTHREAD_H
#include <QObject>
#include <QThread>
class MyWorkerThread : public QThread
{
Q_OBJECT
public:
MyWorkerThread(QObject* parent = nullptr);
void stopThread();
signals:
void changeTextOnUI(const QString& text);
private:
void run() override;
bool m_exit{false};
};
#endif // MYWORKERTHREAD_H
MyWorkerThread.cpp
#ifndef MYWORKERTHREAD_H
#define MYWORKERTHREAD_H
#include <QObject>
#include <QThread>
class MyWorkerThread : public QThread
{
Q_OBJECT
public:
MyWorkerThread(QObject* parent = nullptr);
void stopThread();
signals:
void changeTextOnUI(const QString& text);
private:
void run() override;
bool m_exit{false};
};
#endif // MYWORKERTHREAD_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
MyWorkerThread* worker_thread = new MyWorkerThread();
w.setWorkerThread(worker_thread);
w.connectToSignals();
worker_thread->start();
w.show();
return a.exec();
}
In the above code, you see the full solution to your problem. In the worker thread, we increment a static counter and concatenate it with a string and send it to the master thread that manages the UI elements. We emit a signal and this signal is landed on the slot. This way the master thread updates the text box on the screen per 5 seconds. The worker thread sleeps for 5 seconds at each iteration.
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'm developing C++ application in QT with GUI. To make the GUI always respond, I create a thread for other blocking process. However, the application is waiting for the blocking process and therefore the GUI didn't respond.
Is creating a thread for blocking process is a wrong way?
Or it doesn't work in QT? If so, how to make the GUI respond? Please give me example.
This is a simple example of multithreaded application with responsive GUI:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QLineEdit>
#include <QThread>
class Worker : public QThread
{
protected:
/// Wait 3s which simulates time demanding job.
void run() { sleep(3); }
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
public slots:
void doJob(bool);
void jobFinished();
private:
Worker worker;
QLineEdit *line;
QPushButton *button;
int counter;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
line(new QLineEdit()),
button(new QPushButton()),
counter(0)
{
line->setDisabled(true);
line->setAlignment(Qt::AlignRight);
line->setText(QString::number(counter));
button->setText("Push");
connect(button, SIGNAL(clicked(bool)), this, SLOT(doJob(bool)));
connect(&worker, SIGNAL(finished()), this, SLOT(jobFinished()));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(line);
layout->addWidget(button);
QWidget *window = new QWidget();
window->setLayout(layout);
setCentralWidget(window);
}
void MainWindow::doJob(bool)
{
// Only one thread is running at a time.
// If you want a thread pool, the implementation is up to you :)
worker.start();
// Uncomment to wait. If waiting, GUI is not responsive.
//worker.wait();
}
void MainWindow::jobFinished()
{
++counter;
line->setText(QString::number(counter));
}
Qt has a very good multithreading support. You probably do something wrong and we can't help you if you don't provide any code. There are a lot of ways of implementing "responsive" GUI! (Including a lot of ways how to implement another thread!)
I tried to use a simple QTimer object on my window widget so that I can calculate the elapsed time a method takes to complete. But to my astonishment, the timer was blocked until the method completes execution! i.e when the method in question ends, the timer starts ticking!
Here is a sample code to demonstrate what I wrote:
#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_btnTest_clicked();
void OnTimerTick();
private:
Ui::MainWindow *ui;
ulong seconds;
};
#endif // MAINWINDOW_H
And this is the cpp file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv/cv.h"
#include <QTimer>
#include <QtCore>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_btnTest_clicked()
{
QTimer * timer = new QTimer(0);
seconds =0;
connect(timer,SIGNAL(timeout()),this,SLOT(OnTimerTick()));
timer->setInterval(100);
timer->start();
QThread::sleep(5);//simulating a method which takes 5 seconds to complete
//timer->stop();
}
void MainWindow::OnTimerTick()
{
ui->lblElapsedTime->setText(QString::number(++seconds));
}
How can I get the asynchronous behavior, something like what we have in C# i.e. where the Timer runs its own thread of execution?
Update:
Thanks for the clarification, now how can I incorporate Qthreads with the timer, Do I have to inherit from Qthreads and use timer in my child class or do I have to inherit from QTimer and have a thread executed in it! It's really confusing!
This is common behavior for Qt, WinForms, WPF etc.
All UI-related events are executed synchronously one-by-one on the UI thread. Event handlers are not expected to perform long executions to avoid blocking. If you want to execute a long task, you should do it in other thread.
QTimer is designed to raise events on the UI thread. This is good because you are sure that no other event handlers are executing at that moment.
I want to run two threads concurrently. I successfully did the same when I ran a program as a QT console application.
Here's the working code of QT console application for multi threading:-
myobject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
#include <QDebug>
#include <QThread>
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = 0);
void doSetup(QThread &cThread);
void doSetup2(QThread &cThread2);
signals:
public slots:
void doWork();
void doWork2();
};
#endif // MYOBJECT_H
main.cpp:
#include <QtCore/QCoreApplication>
#include <QThread>
#include <myobject.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThread cThread, cThread2;
MyObject cObject, cObject2;
cObject.doSetup(cThread);
cObject.moveToThread(&cThread);
cObject2.doSetup2(cThread2);
cObject2.moveToThread(&cThread2);
cThread.start();
cThread2.start();
qDebug()<<"hello there ";
return a.exec();
}
my object.cpp
#include "myobject.h"
#include <QThread>
#include "tftpServer.h"
MyObject::MyObject(QObject *parent) :
QObject(parent)
{
}
void MyObject::doSetup(QThread &cThread)
{
connect(&cThread, SIGNAL(started()), this, SLOT(doWork()));
}
void MyObject::doSetup2(QThread &cThread2)
{
connect(&cThread2, SIGNAL(started()), this, SLOT(doWork2()));
}
void MyObject::doWork()
{
for(int i=0; i<1000; i++)
{
qDebug()<<"******************Thread 1";
}
}
void MyObject::doWork2()
{
for(int i=1000; i<2000; i++)
{
qDebug()<<"Thread 2************************";
}
}
Here's the output:
******************Thread 1
Thread 2************************
******************Thread 1
Thread 2************************
******************Thread 1
Thread 2************************
******************Thread 1
Thread 2************************
..and so on
Now when I use this almost same code and run as a QT GUI application, the threads do not run concurrently, but run one after the other. Here's the code:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDebug>
#include <QThread>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void doSetup(QThread &cThread);
void doSetup2(QThread &cThread2);
private:
Ui::MainWindow *ui;
public slots:
void doWork();
void doWork2();
};
#endif // MAINWINDOW_H
main.cpp
#include <QtGui/QApplication>
#include "mainwindow.h"
#include <QThread>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QThread cThread, cThread2;
MainWindow cObject, cObject2;
cObject.doSetup(cThread);
cObject.moveToThread(&cThread);
cObject2.doSetup2(cThread2);
cObject2.moveToThread(&cThread2);
cThread.start();
cThread2.start();
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include <qthread.h>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug()<<"gui running";
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::doSetup(QThread &cThread)
{
connect(&cThread, SIGNAL(started()), this, SLOT(doWork()));
}
void MainWindow::doSetup2(QThread &cThread2)
{
connect(&cThread2, SIGNAL(started()), this, SLOT(doWork2()));
}
void MainWindow::doWork()
{
//QThread::sleep(100);
for(int i=0; i<1000; i++)
{
qDebug()<<"******************Thread 1";
// qDebug()<<i;
}
}
void MainWindow::doWork2()
{
for(int i=1000; i<2000; i++)
{
qDebug()<<"Thread 2************************";
// qDebug()<<i;
}
}
Here's the output:
gui running
gui running
QObject::moveToThread: Widgets cannot be moved to a new thread
QObject::moveToThread: Widgets cannot be moved to a new thread
gui running
******************Thread 1
******************Thread 1
******************Thread 1
******************Thread 1
(and so on...)
******************Thread 1
Thread 2************************
Thread 2************************
Thread 2************************
Thread 2************************
Thread 2************************
Thread 2************************
(and so on...)
Note that the only difference b/w console and gui application I made is changed the base class QObject to QMainWindow, and hence the thread objects so created are objects of QObject & QMainWindow respectively, for console and GUI applications.
Hoping I am sufficiently clear, can you please tell me what mistake am I committing, which is making the threads run one after the other and not concurrently, as it does for the console application?
Thank you.
Any class inherited from QWidget cannot reside in thread other than the main GUI thread. Actually this is what the output said. Your solution is a bad design. Separate the work objects (inherited from QObject, residing in another thread) and the visual objects (inherited from QWidget, residing in the GUI thread), to follow the "one class one responsibility" principle.
Another catch in multithreading in Qt is the fact that any QObject cannot reside in different thread than its parent. This is natural because the parent is the owner of the child object, most importantly it takes care of its destruction. To enable this, the child cannot be in other thread. Otherwise the parent might destroy an executing child, for example.
There are yet more catches, for example QPixmap cannot be in other thread than the main GUI etc.