Qt扫盲-QTimer定时器理论总结

一、概述

QTimer 其实就是一个定时器工具类,定时器就是间隔一定时间后,去执行某一个任务,这个在很多场景都会使用的,我经常使用的即是弹窗自动关闭,就是消息框自动关闭之类的功能。

Qt封装的定时器是使用很简单的,QTimer 类为定时器提供了高级编程接口。要使用它,需要创建一个QTimer,将其timeout()信号连接到适当的槽位,然后调用start()。从那时起,它将以固定的时间间隔发出timeout()信号。

二、使用

1. 使用定时器

定时器有两种使用的方法,一个是实例化对象,一个是用静态函数

  • 方法一:

1秒(1000毫秒)定时器的例子(来自模拟时钟的例子):

QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, QOverload<>::of(&AnalogClock::update));
timer->start(1000);

从那时起,update()槽每秒钟被调用一次。
你可以通过调用setSingleShot(true)将计时器设置为只超时一次。

  • 方法二:

您还可以使用静态QTimer::singleShot()函数,这个函数非常方便,,我们不需要去实现 timerEvent 或创建一个本地QTimer对象。在指定的间隔后调用插槽:

QTimer::singleShot(200, this, SLOT(updateCaption()));

一个简单例子

  #include <QApplication>
  #include <QTimer>

  int main(int argc, char *argv[])
  {
      QApplication app(argc, argv);
      QTimer::singleShot(600000, &app, SLOT(quit()));
      ...
      return app.exec();
  }

2. 多线程注意事项

在多线程应用程序中,可以在任何具有事件循环的线程中使用QTimer。要从非gui线程启动事件循环,请使用QThread::exec()。Qt使用定时器的线程亲和性来确定哪个线程将发出timeout()信号。因此,必须在线程中启动和停止定时器;不可能从另一个线程启动计时器。

3. “0” 定时器

作为一种特殊情况,超时为0的QTimer将尽快超时,尽管0定时器与其他事件源之间的顺序没有指定。Zero定时器可以用来完成一些工作,同时仍然提供一个简洁的用户界面:

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(processOneThing()));
timer->start();

从那时起,processOneThing()将被反复调用。它应该被编写成总是快速返回的方式(通常是在处理完一个数据项之后),这样Qt就可以将事件发送到用户界面,并在完成所有工作后立即停止定时器。这是在GUI应用程序中实现繁重工作的传统方式,但随着多线程在越来越多的平台上变得可用,我们希望零毫秒QTimer对象将逐渐被QThreads取代。

4. 定时器精度和定时器分辨率

定时器的准确性依赖于底层操作系统和硬件。大多数平台支持1毫秒的分辨率,尽管计时器的精度在许多现实世界的情况下并不等于这个分辨率。

精度还取决于定时器的类型。对于Qt::PreciseTimer, QTimer将尝试保持1毫秒的精度。精确定时器永远不会比预期提前超时。

对于Qt::CoarseTimer和Qt::VeryCoarseTimer类型,QTimer可能比预期唤醒时间更早,在这些类型的间隔内:Qt::CoarseTimer间隔的5%,Qt::VeryCoarseTimer间隔的500毫秒。
如果系统繁忙或无法提供所要求的精度,所有定时器类型都可能比预期晚超时。在这种超时超时的情况下,Qt只会触发一次timeout(),即使多次超时都已经过期,然后恢复原来的时间间隔。

5. QTimer的替代方案

第 3点 总的来说,QTimer是最优的定时器方案,有现成的封装的很完善的功能,就用 QTimer就行。

使用QTimer的另一种选择是为你的对象调用QObject::startTimer(),并在你的类(必须继承QObject)中重新实现QObject::timerEvent()事件处理程序。缺点是timerEvent()不支持诸如单触发定时器或信号之类的高级特性。

另一个替代方法是QBasicTimer。这通常比直接使用QObject::startTimer()要简单得多。

  • 替代方法一:
class MyObject : public QObject
  {
      Q_OBJECT

  public:
      MyObject(QObject *parent = nullptr);

  protected:
      void timerEvent(QTimerEvent *event) override;
  };

  MyObject::MyObject(QObject *parent)
      : QObject(parent)
  {
      startTimer(50);     // 50-millisecond timer
      startTimer(1000);   // 1-second timer
      startTimer(60000);  // 1-minute timer

      using namespace std::chrono;
      startTimer(milliseconds(50));
      startTimer(seconds(1));
      startTimer(minutes(1));

      // since C++14 we can use std::chrono::duration literals, e.g.:
      startTimer(100ms);
      startTimer(5s);
      startTimer(2min);
      startTimer(1h);
  }

  void MyObject::timerEvent(QTimerEvent *event)
  {
      qDebug() << "Timer ID:" << event->timerId();
  }

一些操作系统会限制可能使用的定时器的数目。Qt试图绕过这些限制。

3、三种定时器综述

QObject是所有Qt对象的基类,它在Qt中提供了基本的定时器支持。使用QObject::startTimer(),你可以以毫秒为参数启动一个定时器。该函数返回一个唯一的整数定时器ID。计时器现在会定期触发,直到你显式地用计时器ID调用QObject::killTimer()。

要让这种机制起作用,应用程序必须在事件循环中运行。用QApplication::exec()启动一个事件循环。当定时器触发时,应用程序发送一个QTimerEvent,控制流离开事件循环,直到定时器事件处理完毕。这意味着,当应用程序正在忙着做其他事情时,定时器不能触发。换句话说:定时器的准确性取决于应用程序的粒度。

在多线程应用程序中,可以在任何有事件循环的线程中使用定时器机制。要从非gui线程启动事件循环,请使用QThread::exec()。Qt使用对象的线程亲和性来确定哪个线程将交付QTimerEvent。因此,必须启动和停止对象线程中的所有定时器;不能为另一个线程中的对象启动定时器。

间隔值的上限由可以用有符号整数指定的毫秒数决定(实际上,这是一个刚刚超过24天的周期)。准确性取决于底层操作系统。Windows 2000的精度是15毫秒;我们测试过的其他系统可以处理1毫秒的间隔。

定时器功能的主要API是QTimer。这个类提供了在定时器触发时发出信号的常规定时器,并且继承了QObject,这样它就能很好地适应大多数GUI程序的所有权结构。通常的使用方法是这样的:

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(updateCaption()));
timer->start(1000);

QTimer对象是这个小部件的子对象,因此,当这个小部件被删除时,定时器也被删除。接下来,它的timeout()信号连接到将要完成工作的槽,它的值是1000毫秒,表示它将每秒超时。
QTimer还为单触发定时器提供了一个静态函数。例如:

QTimer::singleShot(200, this, SLOT(updateCaption()));

200毫秒(0.2秒)在这行代码执行后,updateCaption()插槽将被调用。
为了让QTimer工作,应用程序中必须有一个事件循环;也就是说,你必须在某个地方调用QCoreApplication::exec()。计时器事件仅在事件循环运行时才会被传递。

在多线程应用程序中,可以在任何具有事件循环的线程中使用QTimer。要从非gui线程启动事件循环,请使用QThread::exec()。Qt使用定时器的线程亲和性来确定哪个线程将发出timeout()信号。因此,必须在线程中启动和停止定时器;不可能从另一个线程启动计时器。

AnalogClock的实现来看:

AnalogClock::AnalogClock(QWidget *parent)
      : QWidget(parent)
{
      QTimer *timer = new QTimer(this);
      connect(timer, SIGNAL(timeout()), this, SLOT(update()));
      timer->start(1000);
      ...
}

每秒钟,QTimer都会调用QWidget::update()槽来刷新时钟的显示。

如果你已经有了一个QObject子类,并且想要一个简单的优化,你可以使用QBasicTimer而不是QTimer。使用QBasicTimer,您必须在QObject子类中重新实现timerEvent()并在那里处理超时。

  • 6
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太阳风暴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值