My QT application relies upon TimerEvent (startTimer/killTimer) to animation GUI components. Recently, however, I compiled and ran my app on my Mac laptop (as opposed to the Windows desktop computer I was developing on) and found that now everything appears to run/update at half the speed it usually does.
The application is not lagging, it simply appears that the update rate is less frequent than what is was originally. What should I do to guarantee consistent timing with the application on all platforms?
Alternatively, should I be using a different feature for temporary timer events? I would prefer not to, as TimerEvent is unbelievably convenient for integrating update cycles into Widgets, but would be interested if they provide consistent timing.
(basic example code for context):
// Once MyObject is created, counts to 20.
// The time taken is noticeably different on each platform though.
class MyObject: public QObject {
public:
MyObject() {
timerId = startTimer(60);
}
protected:
void timerEvent(QTimerEvent* event) {
qDebug() << (counter++);
if(counter == 20) {
killTimer(timerId);
}
Object::timerEvent(event);
}
private:
int timerId = -1, counter = 0;
}
You are likely seeing problems due to accuracy. QTimer's accuracy varies on different platforms:
Note that QTimer's accuracy depends on the underlying operating system and hardware. The timerType argument allows you to customize the accuracy of the timer. See Qt::TimerType for information on the different timer types. Most platforms support an accuracy of 20 milliseconds; some provide more. If Qt is unable to deliver the requested number of timer events, it will silently discard some.
You could try passing Qt::PreciseTimer
to startTimer
(the default is Qt::CoarseTimer
), but additionally I recommend checking the current timestamp against some start time or against the timestamp of the previous tick. This will allow you to adjust how you handle the varying amounts of time between timer events. This is not dissimilar to how time steps are sometimes handled in games.
For example:
class MyObject: public QObject {
public:
MyObject() {
timerId = startTimer(60, Qt::PreciseTimer);
startTime = std::chrono::steady_clock::now();
}
protected:
void timerEvent(QTimerEvent* event) {
qDebug() << (counter++);
if(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - startTime) / 60 >= 20) {
killTimer(timerId);
}
Object::timerEvent(event);
}
private:
int timerId = -1, counter = 0;
std::chrono::steady_clock::time_point startTime;
}
Another example using QElapsedTimer
:
class MyObject: public QObject {
public:
MyObject() {
timerId = startTimer(60, Qt::PreciseTimer);
elapsedTimer.start();
}
protected:
void timerEvent(QTimerEvent* event) {
qDebug() << (counter++);
if(elapsedTimer.elapsed() / 60 >= 20) {
killTimer(timerId);
}
Object::timerEvent(event);
}
private:
int timerId = -1, counter = 0;
QElapsedTimer elapsedTimer;
}