Why does QTimer::setInterval(0) not work as expected in Qt 6?
-
I have a QTimer in a QOpenGLWidget constructor to call update() continuously:
connect(&timer, &QTimer::timeout, this, [&]() { update(); }); timer.setTimerType(Qt::PreciseTimer); timer.setInterval(0); // This worked in Qt 5 with QGLWidget timer.start();In Qt 5 QGLWidget, setting the interval to 0 made the timer fire as fast as possible. In Qt 6, i get FPS as NAN, and I have to set it to 1 to make it work:
timer.setInterval(1); // Required in Qt 6GLWidget::GLWidget(QWidget* parent) : QOpenGLWidget(parent) { connect(&timer, &QTimer::timeout, this, [&]() { update(); qint64 now = elapsedtimer.elapsed(); qint64 elapsed = now - prevTime; prevTime = now; float fps = 1000.0f / elapsed; wavefrontAccess->SetFps(QString::number(fps)); }); timer.setTimerType(Qt::PreciseTimer); timer.setInterval(1); timer.start(); } -
@jeremy_k @Christian-Ehrlicher @Kent-Dorfman The timer was being fired two times once with 20ms elapsed and once with 0 ms Elapsed time.
Previously in QT 5 with QGlWidget , i was using the same code for timer with no issues at all.For QT 6 with QopenglWidget this is what i had to do.
class GLWidget : public QOpenGLWidget { Q_OBJECT; public: explicit GLWidget(QWidget *parent = 0); void initializeGL() override; void paintGL() override; void RenderLoop(); void resizeGL(int w, int h) override; void animate(); qint64 m_lastTime = 0; int m_frameCount = 0; float m_fps = 0.0f; QElapsedTimer m_elapsedTimer; protected: void showEvent(QShowEvent*) override;I had to get rid of the Timer.
GLWidget::GLWidget(QWidget* parent) : QOpenGLWidget(parent) { }void GLWidget::paintGL() { // Do Opengl Rendering animate(); } void GLWidget::animate() { float deltaTimeSec = m_elapsedTimer.restart() / 1000.0f; static int frameCounter = 0; frameCounter++; if (frameCounter >= 10) { // every 10th frame (~5 times/sec at 50 FPS) float fps = 1.0f / deltaTimeSec; wavefrontAccess->SetFps(QString::number(fps, 'f', 2)); frameCounter = 0; } update(); // keep loop running } void GLWidget::showEvent(QShowEvent*) { update(); // Start the loop once } -
You divide by zero so you get NaN. See the documentation of QTimer. A timeout of 0 exexcutes as soon as possible which can also mean directly.
-
You divide by zero so you get NaN. See the documentation of QTimer. A timeout of 0 exexcutes as soon as possible which can also mean directly.
@Christian-Ehrlicher said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
You divide by zero so you get NaN. See the documentation of QTimer. A timeout of 0 exexcutes as soon as possible which can also mean directly.
What does directly mean here? In a C++ program, the event loop needs to run in order to deliver timer events, even for a direct connection to a zero timer.
PyQt and PySide have mechanisms that delivers events while the interactive interpreter is waiting for input, but the code presented doesn't appear to be python.I agree that divide by zero (or other unexpected condition) is a likely problem.
prevTimedoesn't have an obvious initialization before the first calculation ofelapsed. There is also nothing to suggest thatelapsedtimer.elapsed()can't return the same value twice. -
I'd also do the the connect() AFTER setting the timer params.
-
@jeremy_k @Christian-Ehrlicher @Kent-Dorfman The timer was being fired two times once with 20ms elapsed and once with 0 ms Elapsed time.
Previously in QT 5 with QGlWidget , i was using the same code for timer with no issues at all.For QT 6 with QopenglWidget this is what i had to do.
class GLWidget : public QOpenGLWidget { Q_OBJECT; public: explicit GLWidget(QWidget *parent = 0); void initializeGL() override; void paintGL() override; void RenderLoop(); void resizeGL(int w, int h) override; void animate(); qint64 m_lastTime = 0; int m_frameCount = 0; float m_fps = 0.0f; QElapsedTimer m_elapsedTimer; protected: void showEvent(QShowEvent*) override;I had to get rid of the Timer.
GLWidget::GLWidget(QWidget* parent) : QOpenGLWidget(parent) { }void GLWidget::paintGL() { // Do Opengl Rendering animate(); } void GLWidget::animate() { float deltaTimeSec = m_elapsedTimer.restart() / 1000.0f; static int frameCounter = 0; frameCounter++; if (frameCounter >= 10) { // every 10th frame (~5 times/sec at 50 FPS) float fps = 1.0f / deltaTimeSec; wavefrontAccess->SetFps(QString::number(fps, 'f', 2)); frameCounter = 0; } update(); // keep loop running } void GLWidget::showEvent(QShowEvent*) { update(); // Start the loop once } -
S summit has marked this topic as solved
-
@Christian-Ehrlicher said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
You divide by zero so you get NaN. See the documentation of QTimer. A timeout of 0 exexcutes as soon as possible which can also mean directly.
What does directly mean here? In a C++ program, the event loop needs to run in order to deliver timer events, even for a direct connection to a zero timer.
PyQt and PySide have mechanisms that delivers events while the interactive interpreter is waiting for input, but the code presented doesn't appear to be python.I agree that divide by zero (or other unexpected condition) is a likely problem.
prevTimedoesn't have an obvious initialization before the first calculation ofelapsed. There is also nothing to suggest thatelapsedtimer.elapsed()can't return the same value twice.@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
the event loop needs to run in order to deliver timer events, even for a direct connection to a zero timer.
Why? Where did you read this?
-
@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
the event loop needs to run in order to deliver timer events, even for a direct connection to a zero timer.
Why? Where did you read this?
@Christian-Ehrlicher said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
the event loop needs to run in order to deliver timer events, even for a direct connection to a zero timer.
Why? Where did you read this?
My first introduction to the topic was the zero timer autotest.
-
@Christian-Ehrlicher said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
the event loop needs to run in order to deliver timer events, even for a direct connection to a zero timer.
Why? Where did you read this?
My first introduction to the topic was the zero timer autotest.
@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
My first introduction to the topic
... should be the documentation.
And if you really want to know more than look into the source: https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qtimer.cpp#n368 -
@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
My first introduction to the topic
... should be the documentation.
And if you really want to know more than look into the source: https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qtimer.cpp#n368@Christian-Ehrlicher said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
My first introduction to the topic
... should be the documentation.
I thought you were going to ask for something hard, like a reference within the implementation.
In search of firm wording, there's this:
https://doc.qt.io/qt-6/qtimer.html#detailsAs a special case, a QTimer with a timeout of 0 will time out as soon as possible, though the ordering between zero timers and other sources of events is unspecified. Zero timers can be used to do some work while still providing a snappy user interface
This also hints at the event loop reliance:
https://doc.qt.io/qt-6/qtimer.html#interval-propA QTimer with a timeout interval of 0 will time out as soon as all the events in the window system's event queue have been processed.
If a zero timer was implemented as a direct function call, eg:
void QTimer::start() { if (this->interval() == 0) emit timeout(); }then it wouldn't make sense to say that the ordering was unspecified. The timer would time out before QTimer::start() returned. Further, they would be unsuitable to
do some work while still providing a snappy user interface, because they would preempt the starting of, or a return to the event loop. The code might as well perform this work directly.Finally (finally?), a repeating zero timer could deadlock the system. Eg:
QTimer t1; t1.setInterval(0); t1.setSingleShot(false); QObject::connect(&t1, &QTimer::timeout, [] { qDebug() << "zero timer"; }); t1.start(); qDebug() << "This would be an unreachable statement"; -
@Christian-Ehrlicher said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
My first introduction to the topic
... should be the documentation.
I thought you were going to ask for something hard, like a reference within the implementation.
In search of firm wording, there's this:
https://doc.qt.io/qt-6/qtimer.html#detailsAs a special case, a QTimer with a timeout of 0 will time out as soon as possible, though the ordering between zero timers and other sources of events is unspecified. Zero timers can be used to do some work while still providing a snappy user interface
This also hints at the event loop reliance:
https://doc.qt.io/qt-6/qtimer.html#interval-propA QTimer with a timeout interval of 0 will time out as soon as all the events in the window system's event queue have been processed.
If a zero timer was implemented as a direct function call, eg:
void QTimer::start() { if (this->interval() == 0) emit timeout(); }then it wouldn't make sense to say that the ordering was unspecified. The timer would time out before QTimer::start() returned. Further, they would be unsuitable to
do some work while still providing a snappy user interface, because they would preempt the starting of, or a return to the event loop. The code might as well perform this work directly.Finally (finally?), a repeating zero timer could deadlock the system. Eg:
QTimer t1; t1.setInterval(0); t1.setSingleShot(false); QObject::connect(&t1, &QTimer::timeout, [] { qDebug() << "zero timer"; }); t1.start(); qDebug() << "This would be an unreachable statement";@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
I thought you were going to ask for something hard, like a reference within the implementation.
Please look at my link to the source code.
-
@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
I thought you were going to ask for something hard, like a reference within the implementation.
Please look at my link to the source code.
@Christian-Ehrlicher said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
@jeremy_k said in Why does QTimer::setInterval(0) not work as expected in Qt 6?:
I thought you were going to ask for something hard, like a reference within the implementation.
Please look at my link to the source code.
I overlooked that. Thanks.
QTimer::singleShotImpl() uses a queued connection for the ns == 0 case.
https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qtimer.cpp#n389
QMetaObject::invokeMethodImpl(const_cast<QObject *>(receiver), slotObj, Qt::QueuedConnection, h.parameterCount(), h.parameters.data(), h.typeNames.data(), h.metaTypes.data());