I'm new to StackOverflow and wondering if I'm doing this right:
I'm writing a simple Qt application to test multi-threading (something I am also completely new to). I made a MainWindow that contains widgets, and a class MyThread that subclasses QThread and overrides the run() method.
The application simply displays two buttons, "Start Counter" and "Stop Counter", and a text field. When "start counter" is pressed, a worker thread is created and runs in the background, continuously incrementing a counter in a while loop and signaling the main thread (where the GUI is) with the updated value. When "Stop Counter" is pressed, a signal is sent to the main thread that stops the while loop, and the counter is stopped until "Start Counter" is pressed again.
This works perfectly fine ... but is it the best way? I'm new at this, and read a lot of people saying "don't subclass QThread" and other people saying "subclass QThread", and it's a little bit confusing. If this isn't the best way to implement this sort of thing (run a computationally-intensive loop in a background thread with "start" and "stop" buttons), what is? If I'm doing it wrong, how do I do it right? I don't want to learn the wrong way.
Thank you! And here's the code:
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QMutex>
class MyThread : public QThread
{
Q_OBJECT
public slots:
void stopRunning();
protected:
virtual void run();
signals:
void signalValueUpdated(QString);
private:
bool isRunning;
};
MyThread.cpp
#include "MyThread.h"
#include <QString>
void MyThread::run()
{
qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
static int value=0; //If this is not static, then it is reset to 0 every time this function is called.
isRunning = 1;
while(isRunning == 1)
{
QString string = QString("value: %1").arg(value++);
sleep(1/1000); //If this isn't here, the counter increments way too fast and dies, or something; the app freezes, anyway.
emit signalValueUpdated(string);
}
}
void MyThread::stopRunning()
{
isRunning = 0;
}
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLineEdit>
#include "MyThread.h"
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
private:
//Widgets
QHBoxLayout * boxLayout;
QPushButton * startButton;
QPushButton * stopButton;
QLineEdit * lineEdit;
MyThread thread;
};
#endif
MainWindow.cpp
#include "MainWindow.h"
MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
boxLayout = new QHBoxLayout(this);
startButton = new QPushButton("Start Counter", this);
stopButton = new QPushButton("Stop Counter", this);
lineEdit = new QLineEdit(this);
boxLayout->addWidget(startButton);
boxLayout->addWidget(stopButton);
boxLayout->addWidget(lineEdit);
qDebug("Thread id %d",(int)QThread::currentThreadId());
//When the start button is pressed, invoke the start() method in the counter thread
QObject::connect(startButton,SIGNAL(clicked()),&thread,SLOT(start()), Qt::QueuedConnection);
//When the stop button is pressed, invoke the stop() method in the counter thread
QObject::connect(stopButton,SIGNAL(clicked()),&thread,SLOT(stopRunning()), Qt::QueuedConnection);
//When the counter thread emits a signal saying its value has been updated, reflect that change in the lineEdit field.
QObject::connect(&thread,SIGNAL(signalValueUpdated(const QString&)),lineEdit,SLOT(setText(const QString&)), Qt::QueuedConnection);
}
Most of the time QThread sub-classing is a wrong way to do threading in Qt. I suggest you to read an article about threads, event loops and other which could give you an idea how to use threads in Qt in a better way. But do not listen to anyone who arguing that there is the only one right way to use QThread. There are 2 ways and while subclassing is not needed in general it could be useful sometimes. You just need to use non-subclassing way until you really need to subclass. In your particular case you don't need subclassing.
Replace sleep(1/1000); with msleep(100); Things will be just fine :)
Related
I use synchronously block QThread::sleep() function to do timing, which shows number one after another by the time.
the expected running process is synchronously block current thread 2 seconds, and run the following code to change displayed number, and synchronously block another 2 seconds, and so on..., the thought works well in non GUI program.
but in GUI mode, the label only show 9, which is the last number to be displayed.
what leads to the different outcome of the synchronous blocking function sleep in GUI and non GUI program?
#include <windows.h>
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QThread>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
//slot of start timing button
void Widget::on_pushButton_2_clicked()
{
for(int i=0;i<10;i++){
QThread.sleep(2);
ui->label->setText(QString::number(i));
}
}
The GUI needs to continually verify events such as the mouse, the keyboard, etc. and perform actions if certain conditions are met, that is called eventloop. In the case of sleep() it is a blocking task that does not allow the eventloop to run, generating the GUI to freeze (if you want to verify it, try to change the size of the window), so inside the GUI thread you should avoid to use that kind of functions, the blocking tasks you have to turn them into asynchronous or execute it in another thread.
But the task of the sleep() can be replaced by a QTimer without blocking the GUI:
*.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QTimer>
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_pushButton_2_clicked();
void onTimeout();
private:
Ui::Widget *ui;
int counter;
QTimer timer;
};
#endif // WIDGET_H
*.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QLabel>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
counter = 0;
connect(&timer, &QTimer::timeout, this, &Widget::onTimeout);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_2_clicked()
{
timer.start(2000);
}
void Widget::onTimeout()
{
ui->label->setText(QString::number(counter));
counter++;
if(counter > 10){
counter = 0;
timer.stop();
}
}
Another option is to use QEventLoop with QTimer:
void Widget::on_pushButton_2_clicked()
{
for(int i=0;i<10;i++){
QEventLoop loop;
QTimer::singleShot(2000, &loop, &QEventLoop::quit);
loop.exec();
ui->label->setText(QString::number(i));
}
}
Update:
what leads to the different outcome of the synchronous blocking function sleep in GUI and non GUI program?
If you are creating a non GUI application using Qt you will also have problems, although the effect may be less visible.
In the case of the GUI as I said there is an eventloop that handles events, and among them that of repainting, I mean when you set the new text in the QLabel, this is not painted automatically but Qt decides the right moment. That's why when you use QThread::sleep() you do not have time to update the painting.
Obviously in a non GUI application, the eventloop does not verify many events as the one painted by it does not see the effect, in fact in a script that only prints numbers it does not verify any event.
To notice the problem, let's use the following example:
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [](){
qDebug()<< "hello world";
});
timer.start(1000);
qDebug()<< "start blocking";
QThread::sleep(10);
qDebug()<< "end blocking";
return a.exec();
}
We will see that nothing is printed until the sleep() ends, that is, blocking the eventloop that allows the QTimer to do its work.
Answering your comment:
but what still makes me puzzled is why after finishing executing sleep function, after stopping blocking current thread, the following codes can't run as normal
ui->label->setText(QString::number(i)); this statement, right after sleep function
Asynchronous tasks such as painting have less priority than synchronous tasks, that is to say first Qt will execute the for loop and then just do the asynchronous tasks, so in the for the variable that stores the QLabel text is updated, that is to say 0, 1 , ..., 9, and after that, the task is handed over to the eventloop so that it just paints the last value, that is, the 9.
Note:
You can force the evenloop to update within synchronous execution with QXXXApplication::processEvents(), but this is often considered a bad practice, I only show it so you know it but avoid using it:
for(int i=0;i<10;i++){
QThread.sleep(2);
ui->label->setText(QString::number(i));
QApplication::processEvents();
}
What is the easiest way to play a continuous warning sound that lasts several minutes without affecting the main thread's performance?
I know that QSound can work in asynchronous mode, however, according to here:
Qt: How to play sound witout blocking main thread?
QSound would still bring noticeable affect on the main thread.
Is there any simple solution to this?
As suggested earlier, try to play sound in another thread. But QSound has not enough signals to control it. If you want get deeper control you can use QSoundEffect Try this:
header:
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
signals:
public slots:
void process();
};
#endif // WORKER_H
Cpp
#include "worker.h"
#include <QSound>
Worker::Worker(QObject *parent) :
QObject(parent)
{
}
void Worker::process()
{
QSound *sound = new QSound("path");
sound->setLoops(100);
sound->play();
}
Usage:
QThread *thr = new QThread;
Worker * work = new Worker;
connect(thr, SIGNAL(started()), work, SLOT(process()));
work->moveToThread(thr);
thr->start();
To play it several minutes you can set infinite number of loops and use QTimer (maybe singleShot) to end thread and delete object(music will be stopped too).
I have a qslider to control the zooming of a map like this:
connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int)));
However, because this online-map response relatively slow.
I found that the qslider's response also becomes very slow which means when you slide the slider, it's position won't change until suddenly it jump to the position where you release your mouse.
How could I solve this?
One possible solution to delay processing of your signal is to connect it with slot by using Qt::QueuedConnection.
connect(ui->zoomSlider, SIGNAL(valueChanged(int)), ui->map, SLOT(SetZoom(int)), Qt::QueuedConnection);
With Qt::QueuedConnection emitted valueChanged signal event will be not processed at the time of generation, as it happens with directly connected signals. Event will be added to the event loop queue. This is how Qt::QueuedConnection is implemented inside Qt.
Specially for Nejat to test this approach it's possible to use following code:
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 signalReceived();
void signalReceivedQueued();
void buttonPressed();
signals:
void directConnectedSignal();
void queuedConnectedSignal();
private:
Ui::MainWindow *ui;
};
#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);
connect(this, SIGNAL(directConnectedSignal()), SLOT(signalReceived()), Qt::DirectConnection);
connect(this, SIGNAL(queuedConnectedSignal()), SLOT(signalReceivedQueued()), Qt::QueuedConnection);
connect(ui->pushButton, SIGNAL(pressed()), SLOT(buttonPressed()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::signalReceived()
{
qDebug() << "signalReceived";
}
void MainWindow::signalReceivedQueued()
{
qDebug() << "signalReceivedQueued";
}
void MainWindow::buttonPressed()
{
emit queuedConnectedSignal();
emit directConnectedSignal();
}
If you run code snippet above you will get following output on button press:
signalReceived
signalReceivedQueued
Queued signal is emitted first, but received last. And this can be used in your case to prioritize processing of emitted signals.
However most of all using of queued connection will not help you, because user emits slider event too frequently and UI will freeze in any case. So, I can suggest following:
Determine why exactly UI is freezes, what part of code freezing it.
Try to avoid freezing by asynchronous calls or by moving logic into separate thread, or by using QtConcurrent
If you really can't control the way how map is scaled in your webpage, try to ignore all events generated by the QSlider and react only on last generated in 500 ms interval, for example.
I want to create a program by using Qt framework. The aim is to write a program which uses QThread to show a simple digital clock. but nothing happened when running.
This is the subclass of Qthread for running
paytamtimers.h
#ifndef PAYTAMTIMERS_H
#define PAYTAMTIMERS_H
#include <QThread>
class PaytamTimers:public QThread
{
Q_OBJECT
public:
PaytamTimers();
QString now;
protected:
virtual void run();
private:
QMutex mutex;
QThread *thread;
signals:
void mySignal(QString);
};
#endif // PAYTAMTIMERS_H
and this is the implementation of this.class
paytamtimers.cpp
#include "paytamtimers.h"
#include <QTime>
PaytamTimers::PaytamTimers()
{
this->now="";
this->thread=new QThread(0);
}
void PaytamTimers::run(){
forever{
mutex.lock();
this->now=QTime::currentTime().toString();
this->thread->sleep(1000);
emit mySignal(this->now);
mutex.unlock();
}
}
and this is the implementation of GUI form. This for consist of QLabel and an instance of paytamtimers,just for simplity
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "paytamtimers.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
t=new PaytamTimers();
t->start();
connect(t,SIGNAL(t->mySignal(QString)),this,SLOT(this->now(const QString &string)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::now(const QString &string){
this->ui->label->setText(t->now);
}
You should not hold the mutex while sleeping in the thread. In fact, your mutex is completely unnecessary.
Your connect statement is wrong, as noted by hyde. The this parameter is implied, so you could simply say:
connect(t, SIGNAL(mySignal(QString)), SLOT(now(QString)));
You don't need to use a thread in order to emit periodic time updates.
Your MainWindow could look like below. It'll take care to fire the timer event as close to full second as possible.
class MainWindow : public QWidget {
Q_OBJECT
QBasicTimer m_timer;
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() == m_timer.timerId()) {
QTime t = QTime::currentTime();
m_timer.start(1000 - t.msec(), this);
// round to nearest second
if (t.msec() < 500) t = t.addMsecs(-t.msec()); else t = t.addMSecs(1000-t.msec());
now(t.toString());
}
}
void now(const QString &);
...
public:
MainWindow(QWidget *parent = 0) : QWidget(parent) {
m_timer.start(1000 - QTime::currentTime().msec(), this);
}
};
Your connect statement is wrong, it should be:
connect(t, SIGNAL(mySignal(QString)), this, SLOT(now(QString)));
To catch problems like this, first of all check console output, there you should see a warning about failing connect. And another thing, use Qt Creator autocompletion to fill in SIGNAL and SLOT macros, because it is very easy to make stupid mistakes, which will not be spotted by compiler.
There are a few other funny things, such as the seemingly useless extra QThread* member variable in your QThread subclass, and an unnecessary mutex, but these shouldn't stop things from working.
In general, it seems you're just learning Qt. I'd leave threads for later, they have a lot of small gotchas and details, and worrying about them while also learning the Qt basics will not make life easy.
I would like to run a timer inside a QThread. I have written some code in which i am getting some error during the run time. Please guide me into the right direction. What am I doing wrong?
(Parent is QThread(0x1498d10), parent's thread is QThread(0x11272b0), current thread is QThread(0x1498d10)
mainwindow.h //main .h file
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "mythread.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
MyThread *myt;
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp //main .cpp file
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
myt=new MyThread();
myt->start();
MainWindow w;
}
MainWindow::~MainWindow()
{
delete ui;
}
mythread.h // class for thread
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTimer>
class MyThread:public QThread
{
public:
MyThread();
void run();
QTimer *thr;
public slots:
void slo();
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
MyThread::MyThread()
{
thr= new QTimer();
connect(thr,SIGNAL(timeout()),this,SLOT(slo()));
}
void MyThread::run()
{
thr->start(1000);
}
void MyThread::slo()
{
int i,j=0;
i=i+j;
}
Just my humble opinion - Do not to subclass QThread anymore, when you do not need to.
I think, you just want to run your class in new thread or more probably you do not want to block other tasks. Your class is not thread itself. Subclassing basically means that your class IS what you are subclassing.
In other words: Let QThread do its job and concentrate on your class to do what it should do.
Example: MyClass itself does not know anything about threads. It just do what it has to do. Incrementing value and showing results ( plus some sleep part to show how it can block other functions or gui )
Header file
#include <QTimer>
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(bool willSleep, QString name, QObject *parent = 0);
public slots:
void updateCount();
private:
QTimer *timer;
int count;
bool m_wantToSleep;
};
Implementation
#include "myclass.h"
#include <QDebug>
MyClass::MyClass(bool wantToSleep, QString name, QObject *parent) :
QObject(parent)
{
this->setObjectName(name);
m_wantToSleep = wantToSleep;
count = 0;
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(updateCount()));
timer->start(100);
}
void MyClass::updateCount()
{
++count;
qDebug() << objectName() << " count: " << count;
if (m_wantToSleep)
sleep(1);
}
We have code which does the job.
Now implement more threads - its very simple ( memory management, etc not handled to have simple example )
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QThread *thread1 = new QThread; // First thread
QThread *thread2 = new QThread; // Second thread
thread1->start();
thread2->start();
MyClass *myClass = new MyClass(false, "normal class");
MyClass *mySleepClass = new MyClass(true, "sleeper class");
// Better to implement start slot to start timer ( not implemented )
// connect(thread1, SIGNAL(started), myClass, SLOT(start()));
// but this suffice, because timer will emit first signal after class is moved to another thred
//mySleepClass->moveToThread(thread1);
//myClass->moveToThread(thread1);
}
MainWindow::~MainWindow()
{
delete ui;
}
Now we can play with threads:
Blocking GUI ( of course we do not want this )
Initial example works without using new threads. Objects are in current thread and that's why GUI will be blocked. ( since I use sleep function in one instance )
//mySleepClass->moveToThread(thread1);
//myClass->moveToThread(thread1);
Non blocking GUI
We have two more threads running. Why not to use them. In example QThreads are already running, but they play with nothing. Let's move our instances there, to ensure main loop, where GUI is living will not be blocked anymore.
Magic function is moveToThread
Uncomment lines and you can see, that GUI will not be blocked. Both instances are in new thread. But then again, there is a sleep function so One should be counting faster then other. But it is not. Because they are blocking each other. They are in one thread.
mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread1);
Results in both previous cases should be: ( instances lives in same thread and shares the same event loop, so they are blocking each other )
"normal class" count: 1
"sleeper class" count: 1
"normal class" count: 2
"sleeper class" count: 2
"normal class" count: 3
"sleeper class" count: 3
So move them to separate thread
Now GUI is not blocked, niether instances each other.
mySleepClass->moveToThread(thread1);
myClass->moveToThread(thread2);
Results should be: ( and GUI should not be blocked )
"sleeper class" count: 1
"normal class" count: 1
"normal class" count: 2
"normal class" count: 3
"normal class" count: 4
"normal class" count: 5
Hope It was understandable. As for me, this is more logic aproach then subclassing.
Of course you can create QThread in your MyClass, it is not necessary to create it oustide MyClass, I just wanted to show, that you can create one thread and move there more instances.
For anyone who disagree, I just wanted to say that: MyClass is counter with thread support sounds better then: MyClass is thread with counter ability :)
Your timer does not belong to your thread. You should create it in your run() method or you should call tmer->moveToThread before connecting it to slots, but after thread was started.
Check it: MyThread belongs to your main thread. You create timer in constructor of MyThread - so timer belongs to main thread too. But you are trying to initialize and use it in ::run method, that belongs to other thread.
In order to do this, you need to have event loop in your thread.
From QTimer's man page:
In multithreaded applications, you can use QTimer in any thread that has an event loop. To start an event loop from a non-GUI thread, use QThread::exec(). Qt uses the timer's thread affinity to determine which thread will emit the timeout() signal. Because of this, you must start and stop the timer in its thread; it is not possible to start a timer from another thread.
From QThread's man page:
int QThread::exec () [protected]
Enters the event loop and waits until exit() is called, returning the value that was passed to exit(). The value returned is 0 if exit() is called via quit().
It is necessary to call this function to start event handling.
Also, you need to have Q_OBJECT in your class:
class MyThread:public QThread
{
Q_OBJECT
And finally, as Dmitry noted, you need to create QTimer inside your thread, so the entire cpp file should look like this:
#include "mythread.h"
MyThread::MyThread()
{
}
void MyThread::run()
{
thr= new QTimer();
connect(thr,SIGNAL(timeout()),this,SLOT(slo()));
thr->start(1000);
exec();
}
void MyThread::slo()
{
int i = 0,j=0;
i=i+j;
}
Also, read this document.
I was able to create a simple example that starts a timer within another thread, using lambda functions. Here is the code:
#include <QCoreApplication>
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QDebug>
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
QThread* thread = new QThread(&app);
QObject::connect(thread, &QThread::started, [=]()
{
qInfo() << "Thread started";
QTimer* timer1 = new QTimer(thread);
timer1->setInterval(100);
QObject::connect(timer1, &QTimer::timeout, [=]()
{
qInfo() << "Timer1 " << QThread::currentThreadId();
});
timer1->start();
});
thread->start();
QTimer timer2(&app);
QObject::connect(&timer2, &QTimer::timeout, [=]()
{
qInfo() << "Timer2 " << QThread::currentThreadId();
});
timer2.setInterval(100);
timer2.start();
return app.exec();
}