上一篇博客介绍了如何利用QueryPerformanceCounter()来精确计时。我们 在上一篇博客里看到了,Qt的QTimer类是不能非常精确的定时的。本例介绍一种MFC自己提供的定时器函数timeSetEvent,实现毫秒量级的定时触发。
h文件:
#ifndef QTPRECISETIMER_H
#define QTPRECISETIMER_H
#include <QtWidgets/QMainWindow>
#include "ui_qtprecisetimer.h"
#include <Windows.h>
#include <QDebug>
#include <stdio.h>
#include <mmsyscom.h>
#pragma warning(disable:4996)
#pragma comment(lib, "winmm.lib")
void WINAPI OnTimer(UINT, UINT, DWORD, DWORD, DWORD);
class QtPreciseTimer : public QMainWindow
{
Q_OBJECT
public:
QtPreciseTimer(QWidget *parent = 0);
~QtPreciseTimer();
public slots:
void OnClickStart(void);
private:
Ui::QtPreciseTimerClass ui;
};
#endif // QTPRECISETIMER_H
cpp文件:
#include "qtprecisetimer.h"
MMRESULT g_timerID;
FILE * m_fp = NULL;
LARGE_INTEGER m_nBegin;
LARGE_INTEGER m_nFreq;
QtPreciseTimer::QtPreciseTimer(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(OnClickStart()));
m_fp = fopen("TimerRcd.txt", "w");
}
QtPreciseTimer::~QtPreciseTimer()
{
fclose(m_fp);
}
void QtPreciseTimer::OnClickStart(void)
{
QueryPerformanceFrequency(&m_nFreq);//获取频率
QueryPerformanceCounter(&m_nBegin);//获取起始时间
g_timerID = timeSetEvent(100, 1, (LPTIMECALLBACK)OnTimer, DWORD(50), TIME_PERIODIC);
fprintf(m_fp, "m_timerID = %d\n", (int)g_timerID);
}
void WINAPI OnTimer(UINT uiID, UINT uiMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
static int iCount = 0;
if(iCount < 20)
{
LARGE_INTEGER nTime;
QueryPerformanceCounter(&nTime);//获取时间
//计算从起始时间开始,到当前的时间间隔,单位毫秒
int iInterval = (nTime.QuadPart - m_nBegin.QuadPart) / (double)m_nFreq.QuadPart * 1000;
iCount++;
fprintf(m_fp, "uiID = %d, uiMsg = %d, dwUser = %d, dw1 = %d, dw2 = %d, Time = %d ms\n", uiID, uiMsg, dwUser, dw1, dw2, iInterval);
}
else
{
timeKillEvent(g_timerID);
}
}
运行后的界面:
可见,1)定时误差在1毫秒范围内; 2)回调函数OnTimer的第一个输入参数uiID其实就等于timeSetEvent的返回值,也就是定时器的ID; 3)timeSetEvent()的第一个输入参数 代表定时的间隔,单位毫秒,第二个参数代表定时的分辨率,或者说精确度,第三个参数指向回调函数;第四个参数是一个DWORD型,回调函数的第三个参数的取值与其相等;最后的 参数决定定时器是周期性触发TIME_PERIODIC还是单次触发TIME_ONESHOT 。
结束timer时,调用timeKillEvent()