QObject::startTimer: Timers can only be used with threads started with QThread
Asked Answered
G

4

10

I am trying to start a Timer in a worker thread's event loop, but I get this error: QObject::startTimer: Timers can only be used with threads started with QThread

Whats wrong with this?

#include <QObject>
#include <QThread>
#include <QTimer>

class A : public QObject
{
    Q_OBJECT
public:
    A();

private:
    QThread m_workerThread;
    QTimer m_myTimer;

};

A::A()
{
    this->moveToThread(&m_workerThread);
    m_myTimer.moveToThread(&m_workerThread);
    m_workerThread.start();
    m_myTimer.start(1000);
}
Gaud answered 14/3, 2014 at 8:38 Comment(0)
V
3

Initialize your timer anywhere, but start it right when the thread is started (attach it to QThread::started signal):

class A : public QObject
{
    Q_OBJECT
public:
    A();

private slots:
    void started();
    void timeout();

private:
    QThread m_workerThread;
    QTimer m_myTimer;
};

A::A()
{
    moveToThread(&m_workerThread);

    connect(&m_workerThread, SIGNAL(started()), this, SLOT(started()));
    connect(&m_myTimer, SIGNAL(timeout()), this, SLOT(timeout()));

    m_myTimer.setInterval(1000);
    m_myTimer.moveToThread(&m_workerThread);

    m_workerThread.start();
}

void A::started()
{
    timer.start();
}

void A::timeout()
{
    // timer handler
}
Veinstone answered 4/1, 2018 at 19:1 Comment(0)
G
1

I Think i figured it out, i tried to start the timer from the GUI thread, after i moved it to the worker thread, this way it seems to work:

class A : public QObject
{
    Q_OBJECT
public:
    A();

private:
    QThread m_workerThread;
    QTimer m_myTimer;

public slots:
    void sl_startTimer();
};

A::A()
{
    this->moveToThread(&m_workerThread);
    m_myTimer.moveToThread(&m_workerThread);
    m_workerThread.start();
    QMetaObject::invokeMethod(this, "sl_startTimer", Qt::QueuedConnection);
}

void A::sl_startTimer()
{
    m_myTimer.start(1000);
}
Gaud answered 14/3, 2014 at 8:45 Comment(3)
@SebastianLange The timer should be in the same threads where most of its signal recipients are. Otherwise you'll stall the recipient thread when the emitter thread is stalled.Handtohand
true. just got something wrong in my mind, still was at single shot. But still, if the whole object is located in the thread, i would not need to move timer residing in this object to the other thread too...Trump
@SebastianLange u mean when the QTimer Member instead was a QTimer * and i added a slot like sl_init() where i allocate 'new QTimer(this)'? i guess you are right, but then you have more code. i prefer less code.Gaud
A
0

This approach seems a little dangerous to me. By moving the QObject onto the QThread, you're making the thread responsible for the object's events (signals, slots, messages, etc). When the object is deleted, however, the thread will be deleted before the object itself, which can lead to some unexpected behaviours.

The recommended approach is to instantiate the thread and the object separately.

Ailee answered 15/3, 2014 at 3:18 Comment(0)
M
0

Hope this will be helpful:

class ReadYoloResult : public QObject
{
    Q_OBJECT
public:
    ReadYoloResult(QObject *parent = 0);
    void startTimer();
    QThread workerThread;

private:
    QTimer *timer;

public slots:
    void timerSlot();
};

ReadYoloResult::ReadYoloResult(QObject * parent)
{
    this->moveToThread(&workerThread);
    timer = new QTimer();
    connect(timer,SIGNAL(timeout()),this,SLOT(timerSlot()));

    workerThread.start();

    //timer->start(1000);
}

void ReadYoloResult::startTimer(){
    timer->start(100);
}
void ReadYoloResult::timerSlot(){
    qDebug()<<"In timer slot";
}
Merrymerryandrew answered 10/12, 2019 at 15:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.