Reputation: 321
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();
}
Upvotes: 1
Views: 8021
Reputation: 2789
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()));
}
Upvotes: 5
Reputation: 863
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.
Upvotes: 1