I detail here after some experimentation how QTimer
behaves when the receiver is busy.
Here is the experimentation source code: (add QT += testlib
to the project file)
#include <QtGui>
#include <QtDebug>
#include <QTest>
struct MyWidget: public QWidget
{
QList<int> n; // n[i] controls how much time the i-th execution takes
QElapsedTimer t; // measure how much time has past since we launch the app
MyWidget()
{
// The normal execution time is 200ms
for(int k=0; k<100; k++) n << 200;
// Manually add stalls to see how it behaves
n[2] = 900; // stall less than the timer interval
// Start the elapsed timer and set a 1-sec timer
t.start();
startTimer(1000); // set a 1-sec timer
}
void timerEvent(QTimerEvent *)
{
static int i = 0; i++;
qDebug() << "entering:" << t.elapsed();
qDebug() << "sleeping:" << n[i]; QTest::qSleep(n[i]);
qDebug() << "leaving: " << t.elapsed() << "\n";
}
};
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
MyWidget w;
w.show();
return app.exec();
}
When the execution time is smaller than the time interval
Then as expected, the timer runs steadily shooting every seconds. It does take into account how much time the execution has taken, and then the method timerEvent
always starts at a multiple of 1000ms:
entering: 1000
sleeping: 200
leaving: 1201
entering: 2000
sleeping: 900
leaving: 2901
entering: 3000
sleeping: 200
leaving: 3201
entering: 4000
sleeping: 200
leaving: 4201
When only one click is missed because the receiver was busy
n[2] = 1500; // small stall (longer than 1sec, but less than 2sec)
Then, the next slot is called right away after the stall is finished, but the subsequent calls are still multiple of 1000ms:
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 1500
leaving: 3500 // one timer click is missed (3500 > 3000)
entering: 3500 // hence, the following execution happens right away
sleeping: 200
leaving: 3700 // no timer click is missed (3700 < 4000)
entering: 4000 // normal execution times can resume
sleeping: 200
leaving: 4200
entering: 5000
sleeping: 200
leaving: 5200
It also works if the following clicks are also missed due to the accumulation of time, as long as there is only one click that is missed at each execution:
n[2] = 1450; // small stall
n[3] = 1450; // small stall
output:
entering: 1000
sleeping: 200
leaving: 1201
entering: 2000
sleeping: 1450
leaving: 3451 // one timer click is missed (3451 > 3000)
entering: 3451 // hence, the following execution happens right away
sleeping: 1450
leaving: 4901 // one timer click is missed (4901 > 4000)
entering: 4902 // hence, the following execution happens right away
sleeping: 200
leaving: 5101 // one timer click is missed (5101 > 5000)
entering: 5101 // hence, the following execution happens right away
sleeping: 200
leaving: 5302 // no timer click is missed (5302 < 6000)
entering: 6000 // normal execution times can resume
sleeping: 200
leaving: 6201
entering: 7000
sleeping: 200
leaving: 7201
When more than one click is missed because the receiver was very busy
n[2] = 2500; // big stall (more than 2sec)
If two or more clicks are missed, only then a problem appear. The execution times are not synchronized with the first execution, but rather with the moment the stall finished:
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 2500
leaving: 4500 // two timer clicks are missed (3000 and 4000)
entering: 4500 // hence, the following execution happens right away
sleeping: 200
leaving: 4701
entering: 5500 // and further execution are also affected...
sleeping: 200
leaving: 5702
entering: 6501
sleeping: 200
leaving: 6702
Conclusion
The solution of Digikata has to be used if the stalls might be longer than twice the timer interval, but otherwise it is not needed, and the trivial implementation as above works well. In the case you'd rather have the following behaviour:
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 1500
leaving: 3500 // one timer click is missed
entering: 4000 // I don't want t execute the 3th execution
sleeping: 200
leaving: 4200
Then you can still use the trivial implementation, and just check that enteringTime < expectedTime + epsilon
. If it is true, take the picture, if it is false, do nothing.