我在进行一些实验后详细介绍了 QTimer
在接收器忙碌时的行为。
以下是实验用的源代码:(将QT += testlib
添加到项目文件中)
#include <QtGui>
#include <QtDebug>
#include <QTest>
struct MyWidget: public QWidget
{
QList<int> n;
QElapsedTimer t;
MyWidget()
{
for(int k=0; k<100; k++) n << 200;
n[2] = 900;
t.start();
startTimer(1000);
}
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();
}
当执行时间小于时间间隔时
那么,预期的是,定时器稳定地每秒拍摄一次。它会考虑执行所花费的时间,然后方法timerEvent
总是从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
当由于接收者忙碌而错过了仅一次点击时
n[2] = 1500; // small stall (longer than 1sec, but less than 2sec)
当一个stall完成后,下一个slot会立即被调用,但是随后的调用仍然是1000ms的倍数:
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 1500
leaving: 3500
entering: 3500
sleeping: 200
leaving: 3700
entering: 4000
sleeping: 200
leaving: 4200
entering: 5000
sleeping: 200
leaving: 5200
如果由于时间累积而错过了接下来的点击,只要每次执行中只有一个点击被错过,这个方法也可以奏效:
n[2] = 1450; // small stall
n[3] = 1450; // small stall
输出:
entering: 1000
sleeping: 200
leaving: 1201
entering: 2000
sleeping: 1450
leaving: 3451
entering: 3451
sleeping: 1450
leaving: 4901
entering: 4902
sleeping: 200
leaving: 5101
entering: 5101
sleeping: 200
leaving: 5302
entering: 6000
sleeping: 200
leaving: 6201
entering: 7000
sleeping: 200
leaving: 7201
当接收者非常忙碌,错过了多次点击时。
n[2] = 2500; // big stall (more than 2sec)
如果错过了两个或更多的点击,
那么问题就会出现。执行时间与第一次执行不同步,而是与停顿结束的时刻同步:
entering: 1000
sleeping: 200
leaving: 1200
entering: 2000
sleeping: 2500
leaving: 4500
entering: 4500
sleeping: 200
leaving: 4701
entering: 5500
sleeping: 200
leaving: 5702
entering: 6501
sleeping: 200
leaving: 6702
结论
必须使用Digikata的解决方案,如果停滞时间可能超过计时器间隔的两倍,否则不需要,并且上述简单实现方法有效。如果您更喜欢以下行为:
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
那么你仍然可以使用简单的实现方式,只需检查 enteringTime < expectedTime + epsilon
是否为真。如果是真的,就拍照;如果是假的,则不进行任何操作。