QTimer 和 QElapsedTimer

QTimer 是软件定时器,其父类是 QObject。QTimer 的主要功能是设置以毫秒为单位的定时周期,然后进行连续定时或单次定时。启动定时器后,定时溢出时 QTimer 会发射 timeout() 信号,为 timeout()信号关联槽函数就可以进行定时处理。

QElapsedTimer 用于快速计算两个事件的间隔时间,是软件计时器。QElapsedTimer 没有父类,其计时精度可以达到纳秒级。QElapsedTimer的主要用途是比较精确地确定一段程序运行的时长。

一. QTimer 类

QTimer 的父类是 QObject,支持 Qt 的元对象系统。所以,QTimer 虽然不是一个界面组件类, 但是它也有属性、信号和槽。

1.主要属性和接口函数

QTimer 类的主要属性如表所示:

QTimer 类的主要属性
属性属性值类型功能
intervalint定时周期,单位是毫秒
singleShotbool定时器是否为单次定时,true 表示单次定时
timerTypeQt::TimerType定时器精度类型
activebool只读属性,返回 true 表示定时器正在运行,也就是运行 start()函数启动了定时器
remainingTimeint只读属性,到发生定时溢出的剩余时间,单位是毫秒。若定时器未启动,属性值为−1,若已经发生定时溢出,属性值为 0

属性 timerType 表示定时器的精度类型,设置函数 setTimerType()的原型定义如下:

void QTimer::setTimerType(Qt::TimerType atype) 

参数 atype 是枚举类型 Qt::TimerType,有以下几种枚举值,默认值是 Qt::CoarseTimer。

• Qt::PreciseTimer:精确定时器,精度尽量保持在毫秒级。

• Qt::CoarseTimer:粗糙定时器,定时误差尽量在定时周期值的5%以内。

• Qt::VeryCoarseTimer:非常粗糙的定时器,精度保持在秒级。

QTimer 有几个公有槽函数用于启动和停止定时器,其函数原型定义如下:

void QTimer::start() //启动定时器
void QTimer::start(int msec) //启动定时器,并设置定时周期为 msec,单位是毫秒
void QTimer::stop() //停止定时器

2.timeout()信号

QTimer 只有一个 timeout()信号,其原型定义如下:

void QTimer::timeout() 

用函数 start()启动定时器后,定时溢出时 QTimer 就会发射 timeout()信号。如果是连续定时, 就会周期性地定时溢出和周期性地发射 timeout()信号。如果是单次定时,只会发生一次定时溢出和发射一次timeout()信号。要对定时溢出事件进行处理,需要编写一个槽函数与timeout()信号关联。

3.静态函数 singleShot()

QTimer 有一个静态函数 singleShot(),用于创建和启动单次定时器,并且将定时器的 timeout() 信号与指定的槽函数关联。这个函数有多种参数形式,其中一种函数原型定义如下:

void QTimer::singleShot(int msec, Qt::TimerType timerType, 
 const QObject *receiver, const char *member) 

其中,参数 msec 是定时周期,单位是毫秒;timerType 是定时器精度类型;receiver 是接收定时器的 timeout()信号的对象;member 是与 timeout()信号关联的槽函数的指针。

例如,用下面的代码创建并启动一个单次定时器,定时周期是 1000 毫秒,do_timer_shot()是在窗口类 Widget 里自定义的一个槽函数,与定时器的 timeout()信号关联。

QTimer::singleShot(1000, Qt::PreciseTimer, this, &Widget::do_timer_shot); 

二. QElapsedTimer 类

QElapsedTimer 用于快速计算两个事件的间隔时间,它没有父类,不支持 Qt 的元对象系统, 所以只有接口函数。QElapsedTimer 的主要接口函数定义如下:

void start() //复位并启动计时器
qint64 elapsed() //返回流逝的时间,单位:毫秒
qint64 nsecsElapsed() //返回流逝的时间,单位:纳秒
qint64 restart() //重新启动计时器

函数 elapsed()的返回值是自上次运行 start()之后计时器的运行时间,单位是毫秒。

函数 nsecsElapsed()的返回值也是自上次运行 start()之后计时器的运行时间,单位是纳秒。

函数 restart()返回从上次启动计时器到现在的时间,单位是毫秒,然后重启计时器。相当于先后运行了 elapsed()和 start()。

QElapsedTimer 的计时精度比较高,可以达到纳秒级,所以它一般用于精确计量一段程序的运行时长,例如通过统计程序运行时长比较算法的性能。

三. 示例程序功能实现

示例的窗口基类是 QWidget。 要特别注意一点,界面最上层布局是垂直布局,为了防止“定时器”和“定时器精度”两个分组框在垂直方向自动放大,将这两个分组框的sizePolicy 属性的 Vertical Policy 设置为 Fixed,因为其默认值是 Preferred。

界面设计用到了 QLCDNumber 组件,这是模仿 LCD 数值显示的组件,可以显示整数或浮点数。 界面上使用了 3 个 QLCDNumber 组件,分别显示小时、分钟、秒数据,显示小时数据的 QLCDNumber 组件的属性如下图所示。

QLCDNumber 的关键属性是 digitCount 和 smallDecimalPoint, digitCount 表示显示的数字位数,smallDecimalPoint 表示是否显示小数点。如果 digitCount 设置为 2,smallDecimalPoint 设置为 false,就只显示两位整数,那么可显示的数值范围是 0~99。界面上 3 个QLCDNumber 组件分别显示小时、分钟、秒数据,最多是两位整数,所以设置的 digitCount 属性都是 2。

由于 QTimer 和 QElapsedTimer 都不是界面组件,因此需要在 Widget 类里定义变量。文件 widget.h 中 Widget 类的定义代码如下:

class Widget : public QWidget 
{ 
 Q_OBJECT 
private: 
 QTimer *m_timer; //定时器
 QElapsedTimer m_counter; //计时器
public: 
 Widget(QWidget *parent = nullptr); 
private slots: 
 void do_timer_timeout(); //自定义槽函数,与定时器的 timeout()信号关联
 void do_timer_shot(); //与单次定时器关联的槽函数
private: 
 Ui::Widget *ui; 
};

Widget 类里定义了两个自定义槽函数。do_timer_timeout()用于与定时器 m_timer 的 timeout() 信号关联,do_timer_shot()用于在使用静态函数 QTimer::singleShot()动态创建单次定时器时作为定 时器的 timeout()信号关联的槽函数。

Widget 类的构造函数代码如下。我们在构造函数里创建了 QTimer 对象 m_timer,并且将它的 timeout()信号与自定义槽函数 do_timer_timeout()关联。

Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) 
{ 
 ui->setupUi(this); 
//“定时器”和“定时器精度”分组框在垂直方向上设置为固定尺寸
 ui->groupBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); 
 ui->groupBox_3->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); 
 m_timer= new QTimer(this); //创建定时器
 m_timer->stop(); //先停止定时器
 m_timer->setTimerType(Qt::CoarseTimer); //定时器精度
 ui->radioCoarse->setChecked(true); 
 connect(m_timer, SIGNAL(timeout()), this, SLOT(do_timer_timeout())); 
}

 点击“开始”和“停止”按钮分别可以控制定时器 m_timer 的启动和停止。与定时器 m_timer 操作相关的两个按钮的槽函数以及自定义槽函数 do_timer_timeout()的代码如下:

void Widget::on_btnStart_clicked() 
{//“开始”按钮
 m_timer->setInterval(ui->spinBoxIntv->value()); //设置定时器的周期
 if (ui->radioContiue->isChecked()) 
 m_timer->setSingleShot(false); //设置为连续定时
 else 
 m_timer->setSingleShot(true); //设置为单次定时
 //设置定时器精度
 if (ui->radioPrecise->isChecked()) 
 m_timer->setTimerType(Qt::PreciseTimer); 
 else if (ui->radioCoarse->isChecked()) 
 m_timer->setTimerType(Qt::CoarseTimer); 
 else 
 m_timer->setTimerType(Qt::VeryCoarseTimer); 
 m_timer->start(); //启动定时器
 m_counter.start(); //启动计时器
 ui->btnStart->setEnabled(false); 
 ui->btnOneShot->setEnabled(false); 
 ui->btnStop->setEnabled(true); 
} 
void Widget::on_btnStop_clicked() 
{//“停止”按钮
 m_timer->stop(); //定时器停止
 int tmMsec= m_counter.elapsed(); //流逝的时间,单位:毫秒
 int ms= tmMsec % 1000; //余数,单位:毫秒
 int sec= tmMsec/1000; //单位:整秒
 QString str= QString("流逝的时间:%1 秒,%2 毫秒").arg(sec).arg(ms,3,10,QChar('0')); 
 ui->labElapsedTime->setText(str); 
 ui->btnStart->setEnabled(true); 
 ui->btnOneShot->setEnabled(true); 
 ui->btnStop->setEnabled(false); 
} 
void Widget::do_timer_timeout() 
{//与定时器的 timeout()信号关联的槽函数
 QApplication::beep(); //使系统“嘀”一声
 QTime curTime= QTime::currentTime(); //获取当前时间
 ui->LCDHour->display(curTime.hour()); //LCD 显示 小时
 ui->LCDMin->display(curTime.minute()); //LCD 显示 分钟
 ui->LCDSec->display(curTime.second()); //LCD 显示 秒
 if (m_timer->isSingleShot()) //如果是单次定时,显示流逝的时间
 { 
 int tmMsec= m_counter.elapsed(); //毫秒数
 QString str= QString("流逝的时间:%1 毫秒").arg(tmMsec); 
 ui->labElapsedTime->setText(str); 
 ui->btnStart->setEnabled(true); 
 ui->btnOneShot->setEnabled(true); 
 ui->btnStop->setEnabled(false); 
 } 
} 

 do_timer_timeout()是与定时器 m_timer 的 timeout()信号关联的槽函数,每次定时溢出时就会运行这个函数。这个函数的功能是用 3 个 QLCDNumber 组件显示系统当前的时间。如果定时器是单次定时,还会显示流逝的时间,流逝的时间应该等于定时器的定时周期。如果将定时周期设置为 1000ms,采用单次定时,可以测量不同定时器精度下的流逝时间。精度设置为 PreciseTimer 时, 时间误差一般不超过 2ms;精度设置为 CoarseTimer 时误差可达到 20ms。

点击“停止”按钮时,停止连续定时的定时器,并且显示从点击“开始”按钮到点击“停止” 按钮的间隔时间。

 还可以使用静态函数 QTimer::singleShot()启动单次定时器,标题为“动态创建单次定时器” 的按钮的槽函数以及相关的自定义槽函数 do_timer_shot()的代码如下。

void Widget::on_btnOneShot_clicked() 
{//“动态创建单次定时器”按钮
 int intv= ui->spinBoxIntv->value(); //定时周期
 QTimer::singleShot(intv, Qt::PreciseTimer, this, &Widget::do_timer_shot); 
 m_counter.start(); //启动计时器
 ui->btnOneShot->setEnabled(false); 
} 
void Widget::do_timer_shot() 
{//与动态创建的单次定时器的 timeout()信号关联的槽函数
 QApplication::beep(); 
 int tmMsec= m_counter.elapsed(); //流逝的时间,单位:毫秒
 QString str= QString("流逝的时间:%1 毫秒").arg(tmMsec); 
 ui->labElapsedTime->setText(str); 
 ui->btnOneShot->setEnabled(true); 
}

槽函数 on_btnOneShot_clicked()里动态创建并启动了一个单次定时器,核心代码如下:

QTimer::singleShot(intv, Qt::PreciseTimer, this, &Widget::do_timer_shot); 

所创建的单次定时器的定时精度是 Qt::PreciseTimer,可以精确到毫秒,timeout()信号关联的槽函数是窗口类 Widget 中的自定义槽函数 do_timer_shot()。

 当单次定时溢出时,槽函数 do_timer_shot()就会被运行,这个函数里会获取计时器的流逝时间并显示,该时间应该等于动态创建的单次定时器的定时周期。若定时周期设置为 1000ms,显示的流逝时间误差一般不超过 2ms。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值