描述
QObject
是所有Qt对象的基类,提供了Qt中基础的定时器支持。通过QObject::startTimer()
函数,可以使用毫秒为单位的时间间隔来启动一个定时器。该函数返回一个唯一的整数定时器ID。该计时器现在将以规律的间隔触发,直到显式调用QObject::killTimer()
函数并传入计时器ID为止。
为了使此机制工作,应用程序必须在事件循环中运行。可以使用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()
信号,因此必须在其线程中启动和停止计时器;无法从另一个线程启动计时器。
如果已经有了一个QObject
子类并且想要进行简单的优化,可以使用QBasicTimer
而不是QTimer
。使用QBasicTimer
,必须在QObject
的子类中重新实现timerEvent()
函数,并在那里处理超时。
QTimer类
QTimer类
提供了重复的单次计时器。
QTimer类
为计时器提供了一个高级编程接口。要使用它,需要创建一个QTimer
,将其timeout()
信号连接到适当的插槽,然后调用start()
。从那时起,它将以恒定的间隔发出timeout()
信号。
1秒(1000毫秒)定时器的示例(来自模拟时钟示例):
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1000);
从那时起,每秒钟调用一次update()槽。
你可以通过调用setSingleShot(true)
将计时器设置为只超时一次。你也可以使用静态的QTimer::singleShot()
函数在指定的时间间隔后调用一个槽:
QTimer::singleShot(200, this, SLOT(updateCaption()));
在多线程应用程序中,您可以在任何具有事件循环的线程中使用QTimer。要从非gui线程启动事件循环,请使用QThread::exec()
。Qt使用定时器的线程关联来确定哪个线程将发出timeout()
信号。因此,必须在其线程中启动和停止计时器;不可能从另一个线程启动计时器。
作为一种特殊情况,超时为0的QTimer将在处理完窗口系统事件队列中的所有事件后立即超时。这可以用来做繁重的工作,同时提供一个时髦的用户界面:
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(processOneThing()));
timer- > start ();
从那时起,processOneThing()将被反复调用。它应该以这样一种方式编写,即它总是快速返回(通常在处理一个数据项之后),以便Qt可以将事件传递给用户界面并在完成所有工作后立即停止计时器。这是在GUI应用程序中实现繁重工作的传统方式,但是随着多线程在越来越多的平台上变得可用,我们期望零毫秒的QTimer对象将逐渐被qthread所取代。
精度和计时器分辨率
计时器的准确性取决于底层操作系统和硬件。大多数平台支持1毫秒的分辨率,尽管在许多实际情况下计时器的精度不等于这个分辨率。
准确度还取决于计时器的类型。对于Qt:: precistimer, QTimer将尝试将精度保持在1毫秒。精确的计时器也不会比预期更早超时。
对于Qt::CoarseTimer和Qt::VeryCoarseTimer类型,QTimer可能比预期更早唤醒,在这些类型的间隔范围内:Qt::CoarseTimer的间隔为5%,Qt::VeryCoarseTimer的间隔为500毫秒。
如果系统繁忙或无法提供所要求的准确性,所有计时器类型都可能超时,超时时间晚于预期。在这种超时超时的情况下,即使多个超时已经过期,Qt也只会发出一次activated(),然后恢复原来的时间间隔。
QTimer的替代品
使用QTimer的另一种方法是为对象调用QObject::startTimer(),并在类中重新实现QObject::timerEvent()事件处理程序(必须继承QObject)。缺点是timerEvent()不支持单次计时器或信号等高级特性。
另一个选择是QBasicTimer。它通常比直接使用QObject::startTimer()要简单。
一些操作系统限制可以使用的计时器的数量;Qt试图绕过这些限制。
QTimer常用方法
-
std::chrono::milliseconds QTimer::intervalAsDuration() const
以std::chrono::milliseconds对象的形式返回计时器的间隔。 -
bool QTimer::isActive() const
如果计时器正在运行(等待触发)则返回true,否则返回false。
注意:active属性的getter函数。 -
std::chrono::milliseconds QTimer::remainingTimeAsDuration() const
以std::chrono::milliseconds对象的形式返回计时器对象的剩余时间。如果计时器已到期或已超时,则返回std::chrono::milliseconds::zero()。如果找不到剩余时间或计时器未处于活动状态,则该函数返回负持续时间。 -
[static] void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
此静态函数在给定的时间间隔后调用槽。
使用此函数非常方便,因为不需要使用timerEvent或创建本地QTimer对象。
示例:
#include <QApplication>
#include <QTimer>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTimer::singleShot(600000, &app, SLOT(quit()));
...
return app.exec();
}
该示例程序在10分钟后自动终止(600,000毫秒)。
接收者是接收对象,成员是槽。时间间隔为msec毫秒。
-
[static] void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, const char *member)
此函数是重载函数。
此静态函数在给定的时间间隔后调用槽。
使用此函数非常方便,因为您不需要使用timerEvent或创建本地QTimer对象。
接收者是接收对象,成员是槽。时间间隔为msec毫秒。timerType影响计时器的准确性。 -
[static] void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, PointerToMemberFunction method)
此函数是重载函数。
此静态函数在给定的时间间隔后调用QObject的成员函数。
使用此函数非常方便,因为您不需要使用timerEvent或创建本地QTimer对象。
接收者是接收对象,method是成员函数。时间间隔为msec毫秒。timerType影响计时器的准确性。
如果在时间间隔到达之前接收者被销毁,则不会调用该方法。该函数将在接收对象的线程中运行。接收者的线程必须具有运行的Qt事件循环。 -
[static] void QTimer::singleShot(int msec, Functor functor)
此函数是重载函数。
在给定的时间间隔之后,该静态函数将调用functor。
使用此函数非常方便,因为您不需要使用timerEvent或创建本地QTimer对象。
时间间隔为msec毫秒。 -
[static] void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *context, Functor functor)
此函数是重载函数。
在给定的时间间隔之后,该静态函数将调用functor。
使用此函数非常方便,因为您不需要使用timerEvent或创建本地QTimer对象。
时间间隔为msec毫秒。timerType影响计时器的准确性。
如果在时间间隔到达之前上下文被销毁,则不会调用该函数。该函数将在上下文的线程中运行。上下文的线程必须具有运行的Qt事件循环。 -
[static] void QTimer::singleShot(std::chrono::milliseconds msec, const QObject *receiver, const char *member)
此函数是重载函数。
在给定的时间间隔之后,该静态函数将调用槽。
使用此函数非常方便,因为您不需要使用timerEvent或创建本地QTimer对象。
接收者是接收对象,member是槽。时间间隔以duration对象msec的形式给出。 -
[slot] void QTimer::start(int msec)
使用msec毫秒的超时间隔启动或重新启动计时器。
如果计时器已经在运行,则停止并重新启动。
如果singleShot为true,则计时器仅会被激活一次。 -
[slot] void QTimer::start()
该函数重载start()。
使用interval指定的超时间隔启动或重新启动计时器。
如果计时器已经在运行,则停止并重新启动。
如果singleShot为true,则计时器仅会被激活一次。 -
void QTimer::start(std::chrono::milliseconds msec)
这是一种重载函数。
使用持续时间msec毫秒的超时启动或重新启动计时器。
如果计时器已经在运行,则停止并重新启动。
如果singleShot为true,则计时器仅会被激活一次。 -
[slot] void QTimer::stop()
停止计时器。 -
[signal] void QTimer::timeout()
当计时器超时时发出此信号。
注意:这是一个私有信号。它可以用于信号连接,但用户不能发出它。 -
[virtual protected] void QTimer::timerEvent(QTimerEvent *e)
重新实现自QObject::timerEvent()。 -
int QTimer::timerId() const
如果计时器正在运行,则返回计时器的ID;否则返回-1。
QTimerEvent类
QTimerEvent
类包含描述计时器事件的参数。
计时器事件定期发送给启动了一个或多个计时器的对象。每个计时器都有一个唯一的标识符。计时器通过QObject::startTimer()
启动。
QTimer
类提供了一个使用信号而不是事件的高级编程接口。它还提供单次计时器。
事件处理程序QObject::timerEvent()
接收定时器事件。
class MyObject : public QObject
{
Q_OBJECT
public:
MyObject(QObject *parent = 0);
protected:
void timerEvent(QTimerEvent *event);
};
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();
}
定时器示例
下面是一个使用定时器在界面显示当前时间的示例。
.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QTimerEvent>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void timerEvent(QTimerEvent *event);
private:
Ui::MainWindow *ui;
int mTimerId;
};
#endif // MAINWINDOW_H
.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTime>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mTimerId = startTimer(1000);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::timerEvent(QTimerEvent *event)
{
if(event->timerId() == mTimerId)
{
ui->label->setText(QTime::currentTime().toString("hh:mm:ss"));
}
QMainWindow::timerEvent(event);
}
随机数
void qsrand(uint seed)
标准c++ srand()函数的线程安全版本。
设置参数seed,用于生成由qrand()返回的伪随机整数组成的新随机数序列。
每个线程生成的随机数序列是确定的。例如,如果两个线程调用qsrand(1)并随后调用qrand(),则线程将获得相同的随机数序列。
使用时,需要在构造中,添加以下代码,不然每次生成的随机数都是一样的,称为"伪随机"。
qsrand(QTime(0, 0, 0).msecsTo(QTime::currentTime()));
int nRand = qrand()%100+1;
qDebug() << "nRand : " << nRand;
结论
生活不止眼前的苟且,还有未来的苟且
。