Reputation: 3493
I have an object that is derived from QThread and the class definition includes the Q_OBJECT macro. I created a timer within the thread so I can do some occasional checks while the thread is running; however, the timeout event is never occurring.
I've tried making the timer a singleshot as well, but no events are emitted.
Are events processed in a thread by default or do I need to do something else to have them processed?
Here's the code for how I set up the thread and timers:
void MyClass::run( void )
{
checkTimer_chA = new QTimer( this );
qDebug() << connect( checkTimer_chA, SIGNAL( timeout() ), this, SLOT( timerExpiry_chA() ) );
checkTimer_chA->start( 1000 );
// prevent multiple, simultaneous starts
if( !isRunning )
{
qDebug() << "Thread: MyClass::run";
isRunning = true;
while( isRunning )
{
getData();
processData();
yieldCurrentThread();
}
}
checkTimer_chA->stop();
delete checkTimer_chA;
}
void DAQ::timerExpiry_chA( void )
{
qDebug() << "timerExpiry_chA";
checkTimer_chA->stop();
}
If I add QApplication::processEvents(); right before the call to yieldCurrentThread(); the timer works as expected. However, this seems wrong to me.
Upvotes: 2
Views: 10931
Reputation: 3493
Threads do not have their own event loops unless you explicitly create event loops in them. See http://doc.qt.io/qt-5/threads-qobject.html#per-thread-event-loop for details.
Upvotes: 2
Reputation: 10558
Working with threads in Qt can sometimes be a bit of a hassle. This blog post was a real eye opener for me. In case we adopt the style proposed in the blog post to your problem we end up with a solution like below.
Consumer::Consumer():
checkTimer_(new QTimer(this))
{
QObject::connect(checkTimer_, SIGNAL(timeout()), this, SLOT(onTimerExpiration());
QObject::connect(this, SIGNAL(ready()), this, SLOT(consume());
}
bool Consumer::event(QEvent *e)
{
if (e->type() == QEvent::ThreadChange)
{
QTimer::singleShot(0, this, SLOT(start());
}
return QObject::event(e);
}
void Consumer::consume()
{
getData();
processData();
emit ready();
}
void Consumer::start()
{
checkTimer_->start(1000);
emit ready();
}
void Consumer::onTimerExpiration()
{
qDebug() << "timeout";
}
Then run it in a separate thread as follows:
...
Consumer *consumer = new Consumer();
...
QThread *thread = new QThread(this);
thread->start();
consumer->moveToThread(thread);
All child objects of Consumer
will run in the context of the thread Consumer
was moved to. It is possible to create a timeout signal for the Consumer
class and connect it with an object that is not running in thread
. Qt will ensure that the proper signal/slot types are applied to the connection once the object is moved to the thread.
I left out the whole isRunning
part as I doubt you will still need it as long as you only create one Consumer
.
Upvotes: 6
Reputation: 5212
Perhaps you need an event loop running on the thread. What about changing the code to the following?
void MyClass::run( void )
{
checkTimer_chA = new QTimer( this );
qDebug() << connect( checkTimer_chA, SIGNAL( timeout() ), this, SLOT( timerExpiry_chA() ) );
checkTimer_chA->start( 1000 );
// prevent multiple, simultaneous starts
if( !isRunning )
{
qDebug() << "Thread: MyClass::run";
isRunning = true;
QTimer::singleShot(0, this, SLOT(process()));
exec();
}
checkTimer_chA->stop();
delete checkTimer_chA;
}
void MyClass::process()
{
if( isRunning )
{
getData();
processData();
yieldCurrentThread();
QTimer::singleShot(0, this, SLOT(process()));
}
else
QThread::exit();
}
void MyClass::timerExpiry_chA( void )
{
qDebug() << "timerExpiry_chA";
checkTimer_chA->stop();
}
Upvotes: 1