Starting Qtimer in a Qthread

Starting QTimer In A QThread

As I commented (further information in the link) you are doing it wrong :

  1. You are mixing the object holding thread data with another object (responsible of doIt()). They should be separated.
  2. There is no need to subclass QThread in your case. Worse, you are overriding the run method without any consideration of what it was doing.

This portion of code should be enough

QThread* somethread = new QThread(this);
QTimer* timer = new QTimer(0); //parent must be null
timer->setInterval(1);
timer->moveToThread(somethread);
//connect what you want
somethread->start();

Now (Qt version >= 4.7) by default QThread starts a event loop in his run() method. In order to run inside a thread, you just need to move the object. Read the doc...

How to use a QTimer in a separate QThread

A QTimer needs a running event-loop. By default, QThread.run() will start a local event-loop for the thread, but if you completely override it in the way that you have done, that won't happen - so the timer events will never be processed.

In general, when you need a local event-loop you should create a worker object to do all the processing and then use moveToThread to put it in a separate thread. If not, it's perfectly okay to override QThread.run().

The demo below shows how to do this. Note that it's very important to create the timer after the thread has started, otherwise it would be created in the wrong thread and its timer-events wouldn't be processed by the thread's event-loop. It's also important that all communication between the worker thread and the main thread is done via signals, so as to ensure thread-safety. Never try to directly perform GUI operations outside the main thread, as Qt does not support that at all. For the purposes of the demo, a second timer in the main thread is used to stop all processing after a fixed interval. If there was a GUI, user intervention via a button would achieve the same thing.

Demo:

import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class ModbusComWorker(QObject):
finished = pyqtSignal()

def start(self):
self._timer = QTimer(self)
self._timer.timeout.connect(self.process)
self._timer.start(2000)

def stop(self):
self._timer.stop()
self.finished.emit()

def process(self):
print('processing (thread: %r)' % QThread.currentThread())
QThread.sleep(3)

if __name__ == "__main__":

app = QCoreApplication.instance()
if app is None:
app = QApplication(sys.argv)

thread = QThread()
worker = ModbusComWorker()
worker.moveToThread(thread)

def finish():
print('shutting down...')
thread.quit()
thread.wait()
app.quit()
print('stopped')

worker.finished.connect(finish)
thread.started.connect(worker.start)
thread.start()

timer = QTimer()
timer.setSingleShot(True)
timer.timeout.connect(worker.stop)
timer.start(15000)

print('starting (thread: %r)' % QThread.currentThread())

sys.exit(app.exec_())

Output:

starting (thread: <PyQt5.QtCore.QThread object at 0x7f980d096b98>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
processing (thread: <PyQt5.QtCore.QThread object at 0x7f980d0968a0>)
shutting down...
stopped

How to run a timer inside a QThread?

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 :)

Why is a call to QTimer::start() outside a QThread (event loop) not failing?

It seems that you have not understood correctly what the documentation indicates, let's analyze each part of the statement:


In multithreaded applications, you can use QTimer in any thread that has an event loop.

Where did you use the QTimer is there an event loop? Yes, you are using the QTimer in the main thread and you have created the event loop through the QXApplication.

To start an event loop from a non-GUI thread, use QThread::exec()

Is the main thread a non-GUI thread? No, so it is not necessary in this case to use QThread to use a QTimer in the main thread.

In what cases can QTimer fail? If the QTimer runs in the main thread and you have not created a QXApplication, or if you run it in a thread where there is no Qt event loop as std::thread.


Conclusion:

If the QTimer is used in the main thread, just run the QXApplication, if you want to use it in another thread then you must use QThread. In other words, QTimer only works if there is a Qt event loop.

Does a QTimer object run in a separate thread? What is its mechanism?

When I create a QTimer object in Qt 5, and start it using the start()
member function, is a separate thread created that keeps track of the
time and calls the timeout() function at regular intervals?

No; creating a separate thread would be expensive and it isn't necessary, so that isn't how QTimer is implemented.

Here, how does the program know when timeout() occurs?

The QTimer::start() method can call a system time function (e.g. gettimeofday() or similar) to find out (to within a few milliseconds) what the time was that start() was called. It can then add ten milliseconds (or whatever value you specified) to that time and now it has a record indicating when the timeout() signal is supposed to be emitted next.

So having that information, what does it then do to make sure that happens?

The key fact to know is that QTimer timeout-signal-emission only works if/when your Qt program is executing inside Qt's event loop. Just about every Qt program will have something like this, usually near the bottom its main() function:

QApplication app(argc, argv);
[...]
app.exec();

Note that in a typical application, almost all of the application's time will be spent inside that exec() call; that is to say, the app.exec() call will not return until it's time for the application to exit.

So what is going on inside that exec() call while your program is running? With a big complex library like Qt it's necessarily complicated, but it's not too much of a simplification to say that it's running an event loop that looks conceptually something like this:

 while(1)
{
SleepUntilThereIsSomethingToDo(); // not a real function name!
DoTheThingsThatNeedDoingNow(); // this is also a name I made up
if (timeToQuit) break;
}

So when your app is idle, the process will be put to sleep inside the SleepUntilThereIsSomethingToDo() call, but as soon as an event arrives that needs handling (e.g. the user moves the mouse, or presses a key, or data arrives on a socket, or etc), SleepUntilThereIsSomethingToDo() will return and then the code to respond to that event will be executed, resulting in the appropriate action such as the widgets updating or the timeout() signal being called.

So how does SleepUntilThereIsSomethingToDo() know when it is time to wake up and return? This will vary greatly depending on what OS you are running on, since different OS's have different APIs for handling this sort of thing, but a classic UNIX-y way to implement such a function would be with the POSIX select() call:

int select(int nfds, 
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout);

Note that select() takes three different fd_set arguments, each of which can specify a number of file descriptors; by passing in the appropriate fd_set objects to those arguments you can cause select() to wake up the instant an I/O operations becomes possible on any one of a set of file descriptors you care to monitor, so that your program can then handle the I/O without delay. However, the interesting part for us is the final argument, which is a timeout-argument. In particular, you can pass in a struct timeval object here that says to select(): "If no I/O events have occurred after (this many) microseconds, then you should just give up and return anyway".

That turns out to be very useful, because by using that parameter, the SleepUntilThereIsSomethingToDo() function can do something like this (pseudocode):

void SleepUntilThereIsSomethingToDo()
{
struct timeval now = gettimeofday(); // get the current time
struct timeval nextQTimerTime = [...]; // time at which we want to emit a timeout() signal, as was calculated earlier inside QTimer::start()
struct timeval maxSleepTimeInterval = (nextQTimerTime-now);
select([...], &maxSleepTimeInterval); // sleep until the appointed time (or until I/O arrives, whichever comes first)
}

void DoTheThingsThatNeedDoingNow()
{
// Is it time to emit the timeout() signal yet?
struct timeval now = gettimeofday();
if (now >= nextQTimerTime) emit timeout();

[... do any other stuff that might need doing as well ...]
}

Hopefully that makes sense, and you can see how the event loop uses select()'s timeout argument to allow it to wake up and emit the timeout() signal at (approximately) the time that it had previously calculated when you called start().

Btw if the app has more than one QTimer active simultaneously, that's no problem; in that case, SleepUntilThereIsSomethingToDo() just needs to iterate over all of the active QTimers to find the one with the smallest next-timeout-time stamp, and use only that minimum timestamp for its calculation of the maximum time-interval that select() should be allowed to sleep for. Then after select() returns, DoTheThingsThatNeedDoingNow() also iterates over the active timers and emits a timeout signal only for those whose next-timeout-time stamp is not greater than the current time. The event-loop repeats (as quickly or as slowly as necessary) to give a semblance of multithreaded behavior without actually requiring multiple threads.

QThread and QTimer

You don't need to subclass QThread in this particular case. And in general, abstain from subclassing QThread unless you are sure it is what you need.

Here is a quick example how to setup a worker and timer in a thread and launch it:

the worker class:

class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0) : QObject(parent) {}

signals:
void doSomething();

public slots:
void trigger() {
emit doSomething();
}
};

main.cpp

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

MainThreadObject o;

QThread *thread = new QThread;
Worker w;
QTimer timer;
timer.setInterval(1000);

timer.moveToThread(thread);
w.moveToThread(thread);

QObject::connect(thread, SIGNAL(started()), &timer, SLOT(start()));
QObject::connect(&w, SIGNAL(doSomething()), &o, SLOT(doSomething()));
QObject::connect(&timer, SIGNAL(timeout()), &w, SLOT(trigger()));

thread->start();

return a.exec();
}

So, we have the MainThreadObject which represents a QObject derived living in the main thread. We create the timer and Worker object, which is just used to wrap a signal and slot to avoid the need of subclassing QThread. The timer is setup and it and the worker are moved to the new thread, the thread started() signal is connected to the timer start() slot, the worker doSomething() signal is connected to the main thread object doSomething() slot, and finally the timer timeout() signal is connected to the worker trigger() slot. Then the thread is started which initiates the entire chain in the event loop.

As a result, the MainThreadObject::doSomething() is called every second, with the signal emitted from the secondary thread.

QTimer on a Qthread

Answer: in your case I do not see the need to use QThread.

TL; DR;

When do I need to use another thread in the context of a GUI?

Only one thread should be used when some task can block the main thread called the GUI thread, and the blocking is caused because the task is time-consuming, preventing the GUI eventloop from doing its job normally. All modern GUIs are executed in an eventloop that is allows you to receive notifications from the OS like the keyboard, the mouse, etc. and also allows you to modify the status of the GUI depending on the user.

In your case I do not see any heavy task, so I do not see the need for a QThread, I do not really know what the task is that you want to run periodically.

Assuming you have a task that consumes a lot of time, say 30 seconds and you have to do it every half hour, then the thread is necessary. and in your case you want to use a QTimer for it.

Let's go in parts, QTimer is a class that inherits from a QObject, and a QObject belongs to the same as the parent, and if it does not have a parent it belongs to the thread where it was created. On the other hand many times it is thought that a QThread is a thread of Qt, but it is not, QThread is a class that allows to handle the life cycle of a native thread, and that is clearly stated in the docs: The QThread class provides a platform-independent way to manage threads.

Knowing the above, let's analyze your code:

timer = QTimer(self)

In the above code self is the parent of QTimer and self is the QThread, so QTimer belongs to the thread of the parent of QThread or where QThread was created, not to the thread that QThread handles.

Then let's see the code where QThread was created:

worker = WorkerThread()

As we see QThread has no parent, then QThread belongs to the thread where it was created, that is, QThread belongs to the main thread, and consequently its QTimer child also belongs to the main thread. Also note that the new thread that QThread handles only has the scope of the run() method , if the method is elsewhere belongs to the field where QThread was created, with all the above we see that the output of the code is correct, and the QThread.sleep(5) runs on the main thread causing the eventloop to crash and the GUI to freeze.

So the solution is to remove the parent of QTimer so that the thread it belongs to is the one of the run() method, and move the work function within the same method. On the other hand it is a bad practice to create static attributes unnecessarily, considering the above the resulting code is the following:

import sys
import threading
from PyQt5.QtCore import QThread, QTimer
from PyQt5.QtWidgets import QApplication, QPushButton, QWidget

class WorkerThread(QThread):
def run(self):
def work():
print("working from :" + str(threading.get_ident()))
QThread.sleep(5)
print("thread started from :" + str(threading.get_ident()))
timer = QTimer()
timer.timeout.connect(work)
timer.start(10000)
self.exec_()

class MyGui(QWidget):
def __init__(self):
super().__init__()
self.initUi()
self.worker = WorkerThread(self)
print("Starting worker from :" + str(threading.get_ident()))
self.worker.start()

def initUi(self):
self.setGeometry(500, 500, 300, 300)
self.pb = QPushButton("Button", self)
self.pb.move(50, 50)

if __name__ == '__main__':
app = QApplication(sys.argv)
gui = MyGui()
gui.show()
sys.exit(app.exec_())

Output:

Starting worker from :140068367037952
thread started from :140067808999168
working from :140067808999168
working from :140067808999168

Observations:

  • The heavy task that has been emulated is 5 seconds, and that task must be executed every 10 seconds. If your task takes longer than the period you should create other threads.

  • If your task is to perform a periodic task that is not as heavy as showing time then do not use new threads because you are adding complexity to a simple task, besides this may cause the debugging and testing stage to be more complex.

Problems getting QTimer to start when using QThread

If you connect the button's clicked signal to the worker's start slot, Qt will automatically detect that it's a cross-thread connection. When the signal is eventually emitted, it will be queued in the receiving thread's event-queue, which ensures the slot will be called within the worker thread.

However, if you connect the button's clicked signal to the startCapture slot, there's no cross-thread connection, because the slot belongs to MyWidget (which lives in the main thread). When the signal is emitted this time, the slot tries to create the timer from within the main thread, which is not supported. Timers must always be started within the thread that creates them (otherwise Qt will print a message like "QBasicTimer::start: Timers cannot be started from another thread").

A better approach is to connect the started and finished signals of the thread to some start and stop slots in the worker, and then call the thread's start and quit methods to control the worker. Here's a demo based on your script, which shows how to implement that:

from PyQt5 import QtCore, QtWidgets
import sys

class Capture(QtCore.QObject):
def __init__(self, parent=None):
super(Capture, self).__init__(parent)
self.m_timer = QtCore.QBasicTimer()

def start(self):
print("capture start called")
self.m_timer.start(1000, self)

def stop(self):
print("capture stop called")
self.m_timer.stop()

def timerEvent(self, event):
print("time event")

class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
self.btn_start = QtWidgets.QPushButton("Start")
lay.addWidget(self.btn_start)
self.capture = Capture()
self.captureThread = QtCore.QThread(self)
self.capture.moveToThread(self.captureThread)
self.captureThread.started.connect(self.capture.start)
self.captureThread.finished.connect(self.capture.stop)
self.btn_start.clicked.connect(self.startCapture)
self.show()

def startCapture(self):
if not self.captureThread.isRunning():
self.btn_start.setText('Stop')
self.captureThread.start()
else:
self.btn_start.setText('Start')
self.stopCapture()

def stopCapture(self):
self.captureThread.quit()
self.captureThread.wait()

def closeEvent(self, event):
self.stopCapture()

def run_app():
app = QtWidgets.QApplication(sys.argv)
mainWin = MyWidget()
mainWin.show()
app.exec_()

run_app()


Related Topics



Leave a reply



Submit