【问题标题】:TimerEvent has different speed on Windows and MacTimerEvent 在 Windows 和 Mac 上的速度不同
【发布时间】:2018-04-04 08:03:44
【问题描述】:

我的 QT 应用程序依赖 TimerEvent (startTimer/killTimer) 来动画 GUI 组件。然而,最近,我在我的 Mac 笔记本电脑(而不是我正在开发的 Windows 台式电脑)上编译并运行了我的应用程序,发现现在所有东西的运行/更新速度似乎只有平时的一半。

应用程序没有滞后,只是看起来更新率比原来的频率低。我应该怎么做才能保证与所有平台上的应用程序的时序一致?

或者,我应该为临时计时器事件使用不同的功能吗?我不希望这样做,因为 TimerEvent 非常方便地将更新周期集成到小部件中,但如果它们提供一致的时间,我会感兴趣。

(上下文的基本示例代码):

// 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;
}

【问题讨论】:

    标签: c++ qt timer cross-platform qtimer


    【解决方案1】:

    由于准确性,您可能会遇到问题。 QTimer's accuracy varies on different platforms:

    请注意,QTimer 的准确性取决于底层操作系统和硬件。 timerType 参数允许您自定义计时器的准确性。有关不同计时器类型的信息,请参阅 Qt::TimerType。大多数平台支持 20 毫秒的精度;有些提供更多。如果 Qt 无法传递请求数量的计时器事件,它会默默地丢弃一些。

    您可以尝试将Qt::PreciseTimer 传递给startTimer(默认为Qt::CoarseTimer),但另外我建议根据某个开始时间或上一个刻度的时间戳检查当前时间戳。这将允许您调整如何处理计时器事件之间的不同时间量。这与how time steps are sometimes handled in games 没有什么不同。

    例如:

    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;
    }
    

    另一个使用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;
    }
    

    【讨论】:

    • 太棒了!那么快速提问:简单地使用“startTimer(1, Qt::PreciseTimer)”然后在所需时间发生变化之前不运行任何 TimerEvent 内容是否明智?我担心性能,因为事件函数会被如此频繁地调用。最多,我希望 5 个小部件同时运行活动计时器事件。
    • 这对我来说似乎是一个过于激进的间隔,除非你真的需要毫秒级的精度。不过,这取决于您要完成的工作。有关计时器分辨率的讨论,请参见例如this question。他们还指出了一个 QElapsedTimer 类,它可能比我的示例解决方案更合适。
    • 我的逻辑是我对一些小部件使用“startTimer(5)”,所以为了解决 Mac 上的半速问题,我可能需要将其加倍“启动定时器(2)”?在这种情况下,我不妨将其降低到 1。不过,在这些极端示例中,计时器仅运行约 30 个周期。其他一切都是 10 ~ 15,所以对于运行时间较长的间隔,也许 5 就足够了? (感谢其他建议,将使用 QElapsedTimer)
    • 我会测试是否简单地使用我的建议(Qt::PreciseTimer + 计算经过的时间)在实践中为您提供足够好的行为,然后再减少投票时间。这可能就足够了。当然,1ms 也可以,这取决于它的成本,但我倾向于尽量减少轮询频率。
    • 另外请记住,如果 Mac 上粗略计时器的精度不够,通过减少计时器间隔,您观察到的事情以“半速”发生可能根本不会改变。使用精确计时器可能会完全修复它,但根据观察到的时间步长插入计时器事件的行为是同步跨平台行为的最佳机会。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-07
    • 2011-08-09
    • 2013-04-14
    • 1970-01-01
    • 2015-05-29
    • 1970-01-01
    • 2011-02-15
    相关资源
    最近更新 更多