QTimer 其实就是一个定时器工具类,定时器就是间隔一定时间后,去执行某一个任务,这个在很多场景都会使用的,我经常使用的即是弹窗自动关闭,就是消息框自动关闭之类的功能。
Qt封装的定时器是使用很简单的,QTimer 类为定时器提供了高级编程接口。要使用它,需要创建一个QTimer,将其timeout()信号连接到适当的槽位,然后调用start()。从那时起,它将以固定的时间间隔发出timeout()信号。
定时器有两种使用的方法,一个是实例化对象,一个是用静态函数
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
#include
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTimer::singleShot(600000, &app, SLOT(quit()));
...
return app.exec();
}
在多线程应用程序中,可以在任何具有事件循环的线程中使用QTimer。要从非gui线程启动事件循环,请使用QThread::exec()。Qt使用定时器的线程亲和性来确定哪个线程将发出timeout()信号。因此,必须在线程中启动和停止定时器;不可能从另一个线程启动计时器。
作为一种特殊情况,超时为0的QTimer将尽快超时,尽管0定时器与其他事件源之间的顺序没有指定。Zero定时器可以用来完成一些工作,同时仍然提供一个简洁的用户界面:
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(processOneThing()));
timer->start();
从那时起,processOneThing()将被反复调用。它应该被编写成总是快速返回的方式(通常是在处理完一个数据项之后),这样Qt就可以将事件发送到用户界面,并在完成所有工作后立即停止定时器。这是在GUI应用程序中实现繁重工作的传统方式,但随着多线程在越来越多的平台上变得可用,我们希望零毫秒QTimer对象将逐渐被QThreads取代。
定时器的准确性依赖于底层操作系统和硬件。大多数平台支持1毫秒的分辨率,尽管计时器的精度在许多现实世界的情况下并不等于这个分辨率。
精度还取决于定时器的类型。对于Qt::PreciseTimer, QTimer将尝试保持1毫秒的精度。精确定时器永远不会比预期提前超时。
对于Qt::CoarseTimer和Qt::VeryCoarseTimer类型,QTimer可能比预期唤醒时间更早,在这些类型的间隔内:Qt::CoarseTimer间隔的5%,Qt::VeryCoarseTimer间隔的500毫秒。
如果系统繁忙或无法提供所要求的精度,所有定时器类型都可能比预期晚超时。在这种超时超时的情况下,Qt只会触发一次timeout(),即使多次超时都已经过期,然后恢复原来的时间间隔。
第 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试图绕过这些限制。
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()并在那里处理超时。