dwj
dwj

Reputation: 3493

QTimer in a thread - events not processing?

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

Answers (3)

dwj
dwj

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

Ton van den Heuvel
Ton van den Heuvel

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

baysmith
baysmith

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

Related Questions