简体   繁体   中英

QTimer doesn't call timeout slot

I was trying to use a QTimer within different QThread , but I couldn't connect to the QTimer 's timeout() slot.

What am I doing wrong?

Here is my code:

extern MainWindow *mainClass;

class myObj : public QObject
{
    Q_OBJECT
public:
    explicit myObj(QObject *parent = 0);
    ~myObj();
    QThread workerThread;

    int percent;
    QTimer *percentTimer;

public slots:
    void doWork();
    void percentUpdate();
    void startFunction();

signals:
    void start();
    void UpdateResult();
};

myObj::myObj(QObject *parent) : QObject(parent)
{
    moveToThread(&workerThread);

    connect(&workerThread, SIGNAL(finished()), this, SLOT(deleteLater()));
    connect(this, SIGNAL(UpdateResult()), mainClass, SLOT(on_UpdateResult()));
    connect(&workerThread, SIGNAL(started()), this, SLOT(doWork()));
    connect(this, SIGNAL(start()), this, SLOT(startFunction()));

    percent++;
    percentTimer = new QTimer();
    percentTimer->moveToThread(&workerThread);
    percentTimer->setInterval(1000);
    connect(percentTimer, SIGNAL(timeout()), this,SLOT(percentUpdate()));

}

myObj::~myObj() {
    workerThread.quit();
    workerThread.wait();
    if (percentTimer) percentTimer->deleteLater();
}

void myObj::doWork()
{
    emit start();
    workerThread.exec();    
}

void myObj::startFunction()
{
    percentTimer->start();
    QThread::sleep(60);
    percentTimer->stop();    
}

void myObj::percentUpdate()
{
    qDebug() << "In Timer" << percent++;
    emit UpdateResult();

}

This is because you are trying to start your QTimer from a different thread than the one that created it. When using QTimer and threads, you should be very careful to create your QTimer in the thread that will control it.

From QTimer class documentation :

In multithreaded applications, you can use QTimer in any thread that has an event loop. To start an event loop from a non-GUI thread, use QThread::exec(). Qt uses the timer's thread affinity to determine which thread will emit the timeout() signal. Because of this, you must start and stop the timer in its thread ; it is not possible to start a timer from another thread.

In your case percentTimer = new QTimer(); is executed from the main thread, (even though you used moveToThread before, this is still the main thread executing it), while your doWork and start signals are emitted from workerThread .

You can for instance do your new QTimer from a void init() slot called from your workerThread , rather than in the constructor, to ensure that the QTimer is created and owned by the proper thread.

myObj::myObj(QObject *parent) : QObject(parent), percentTimer(nullptr)
{
    moveToThread(&workerThread);

    connect(&workerThread, SIGNAL(finished()), this, SLOT(deleteLater()));
    connect(this, SIGNAL(UpdateResult()), mainClass, SLOT(on_UpdateResult()));
    connect(&workerThread, SIGNAL(started()), this, SLOT(init()));
    connect(&workerThread, SIGNAL(started()), this, SLOT(doWork()));
    connect(this, SIGNAL(start()), this, SLOT(startFunction()));

    percent++;
}

void myObj::init() {
    percentTimer = new QTimer();
    percentTimer->setInterval(1000);
    connect(percentTimer, SIGNAL(timeout()), this, SLOT(percentUpdate()));
}

First of all, you should shart workerThread to let myObj work in its context (start it after creating required connections because slots connected to start signal possibly would not be executed otherwise). Instead of using QThread::sleepFor you should use something like this:

QEventLoop loop;
QTimer::singleShot(60000, &loop, SLOT(exit()));
loop.exec();

to create delays, because QThread::sleepFor freezes overall thread execution. And so none of your events will be processed that bound to this thread.

workerThread.exec() is useless here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM