views:

57

answers:

4

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.

A: 

http://doc.qt.nokia.com/4.6/threads-qobject.html#signals-and-slots-across-threads

Martin Beckett
This signal and slot should be within the same thread; the timer and the connection are both made inside the thread's *run* function.
dwj
This is actually what I want: http://doc.qt.nokia.com/4.6/threads-qobject.html#per-thread-event-loop
dwj
+1  A: 

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();
}
baysmith
This will probably not work since the thread never gets the chance to return to its event loop once process() has been called.
Ton van den Heuvel
Perhaps you are right. The code is incomplete. It is unknown what yieldCurrentThread() does. Let's assume that it does not allow the event loop to run. In which case, I've altered the code above to allow it to return to the event loop.
baysmith
@baysmith: yieldCurrentThread() is a Qt call -- http://doc.trolltech.com/4.6/qthread.html#yieldCurrentThread
dwj
+1  A: 

Threads do not have their own event loops unless you explicitly create event loops in them. See http://doc.qt.nokia.com/4.6/threads-qobject.html#per-thread-event-loop for details.

dwj
A: 

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(this);
...
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.

Ton van den Heuvel