Background: In our Embedded QT application(based on ARM processor), there is a Qtimer which will timeout for every 20ms. Updating the user interface objects is done inside the timeout handler of this timer.
Also, we have a computationally heavy task which I replaced with a "for" loop with some 300000 iterations and print statements inside it for easy understanding. I'm showing BusyIndicator during this operation.
Issue1: When the BusyIndicator is being shown, the Qtimer which is set for 20ms is getting timeout for every 40+ seconds.(Anyway this is not causing any issue at this point of time because, no UI related operations are done when BusyIndicator is displayed).
Issue2(Actual Issue): Once the heavy task is completed, the BusyIndicator is disabled. The Qtimer that is fired after disabling BusyIndicator, also takes 40+ sec to timeout for the first time, then it is recovering and firing for every 20ms subsequently. Because of this, as soon as the BusyIndicator is disabled and any valid page is displayed, the UI is not responding for those 40+ seconds.
Question:
- Why BusyIndicator is affecting the Qtimer's timeout value?
- Is there any way where the BusyIndicator does not affect the Qtimer timeout value?
- Any other solution for the issue that I'm facing?
Note: I created a dummy project in QtCreator and created a demo Desktop based application(in both Windows and Ubuntu) with the same scenario(i.e QTimer being interrupted for some time by a blocking heavy task), I could not see any issue(QTimer fires immediately after the blocking task is completed). Please find below the code of demo application that simulated the scenario of my issue:
main.cpp
#include "main.h"
myClass *classPtr;
static int loopStart=0;
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
myClass classs;
classPtr = &classs;
return app.exec();
}
myClass::myClass(QObject *parent):
QObject(parent)
{
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &myClass::timerEvnt);
timer->start(20);
}
void myClass::timerEvnt()
{
myClass::looop();
if(loopStart >= 5)
qDebug()<<"timer continuation";
loopStart++;
}
void myClass::looop()
{
int j=0;
if(loopStart == 5)
{
for(int i=0; i<300000;i++)
{
qDebug()<<"iterations="<<i;
}
qDebug()<<"#####################for loop comp="<<j;
}
}
main.h
#ifndef MAIN_H
#define MAIN_H
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QObject>
#include <QTimer>
#include <QQuickView>
#include <QElapsedTimer>
class myClass : public QObject
{
Q_OBJECT
public:
QTimer *timer;
explicit myClass(QObject* parent = nullptr);
void timerEvnt();
void looop();
};
#endif // MAIN_H
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Rectangle {
width: parent.width
height: parent.height
Text {
id: displayText
anchors.centerIn: parent
text: "Initial Message"
font.pixelSize: 20
}
}
}
Thanks in Advance
The best approach to responsiveness should not block or delay your UI any more than it needs to.
In your code, you are executing 300 qDebug() statements every 20ms. In other words, you are printing 150000 lines a second. In addition, this all happens on the same thread that the QML engine is on. So, hence, other things the QML engine which includes the
BusyIndicator
animation is being affected.You need to structure your code to output at a rate that we are happy to process the results and is much more friendlier to the QML engine thread, e.g. 200ms interval or 5 times a second.
Consider the following code.
Here, regardless of the platform (could be intel PC, could be an embedded ARM platform), we guarantee that the
Timer
code will only utilize 100ms of aTimer
on a 200ms interval. i.e. we capped it to use no more than 50% of the QML engine thread, giving the code ample time to catch up.We also update the UI/UX at most once every 200ms or 5 times a second.
The number of iterations on a
Timer
will vary, and that's okay. On fast intel PC platforms it can run more iterations on slower ARM-embedded devices it will run less. But, we guarantee that the code is cross-platform friendly and scalable.You can Try this code online!
In C++, the above pattern would be translated as: