起源
在newsmth上看到这样一个问题:
发信人: hgoldfish (老鱼), 信区: KDE_Qt
标 题: QTimer::singleShot()的疑问
发信站: 水木社区 (Mon Apr 11 22:03:48 2011), 站内
singleShot(0, ...)是表示下面的哪种情况呢?
1. 退出当前函数,回到事件循环的时候立即执行,忽略其它消息。
2. 把对应的QTimerEvent放到消息队列的最后,然后依次处理消息。
3. 把这个这个singleShot对应的QTimerEvent放到最后。依次处理消息。但是如果有新的消息到达时,它们会排到QTimerEvent的前面。
其中,2是假定消息队列没有优先级。1、3假定消息队列有优先级,但是1假定QTimerEvent最优先,而3假定QTimerEvent最不优先。
看文档似乎是3?
似乎挺有意思,于是,打开Qt的源码,慢慢看看,于是整理出本文。如果理解没问题的话,应该可以得出这个结论:
一段废话,作为正文引子
每当需要一个计时器时,我们很容易想到QTimer,
- 创建QTimer对象
- 连接它的信号到我们的槽函数。
如果看Qt的Manual,我们还会注意到QBasicTimer和QTimeLine这两个类,也可起到计时的作用。那么这些之间那个最为根本呢?
所有这些都归结到QObject的3个成员函数中:
- 在派生类中覆盖timerEvent()函数,进行处理
- 通过startTimer()开启计时器
- 通过killTimer() 结束
下面我们看看QTimer的计时事件是如何一步一步和系统提供的计时器(优先使用多媒体计时器,其次是普通的计时器)联系起来的
QTimer
看一下QTimer的源码,一切都明了了:
class QTimer:public QObject
{
...
public Q_SLOTS:
void start(int msec);
void start();
void stop();
Q_SIGNALS:
void timeout();
protected:
void timerEvent(QTimerEvent *);
...
};
大家应该想得到了(就不贴代码了):
- start() 调用 QObject::startTimer()
- stop() 调用 QObject::killTimer()
- timerEvent() 中发射信号 timeout()
QTimer::singleShot()
这是一个static成员函数,由于只需要一次事件。它其实没有创建QTimer对象,而是使用了一个QSingleShotTimer对象。这个类完整定义很简单
class QSingleShotTimer : public QObject
{
Q_OBJECT
int timerId;
public:
~QSingleShotTimer();
QSingleShotTimer(int msec, QObject *r, const char * m);
Q_SIGNALS:
void timeout();
protected:
void timerEvent(QTimerEvent *);
};
这个没什么什么可介绍的,如果说特点的话,也就是 timerEvent 被调用一次后,就会将自己这个对象删除(呵呵,有点废话哈,调用一次使命就完成了呗)
另外呢,对于时间间隔为0的事件,甚至连QSingleShotTimer都不需要创建,而是直接用invokeMethod去调用相应的slot
:
void QTimer::singleShot(int msec, QObject *receiver, const char *member)
{
if (receiver && member) {
if (msec == 0) {
// special code shortpath for 0-timers
const char* bracketPosition = strchr(member, '(');
if (!bracketPosition || !(member[0] >= '0' && member[0] <= '3')) {
qWarning("QTimer::singleShot: Invalid slot specification");
return;
}
QByteArray methodName(member+1, bracketPosition - 1 - member); // extract method name
QMetaObject::invokeMethod(receiver, methodName.constData(), Qt::QueuedConnection);
return;
}
(void) new QSingleShotTimer(msec, receiver, member);
}
}
在 QMetaObject::invokeMethod的分析中,我们知道:对于QueuedConnection的连接,它最终通过QCoreApplication的postEvent() 函数派发了一个 QMetaCallEvent 事件。
QObject
- 回归正题,看QObject的 startTimer和killTimer做了什么:
int QObject::startTimer(int interval)
{
Q_D(QObject);
d->pendTimer = true; // set timer flag
return d->threadData->eventDispatcher->registerTimer(interval, this);
}
void QObject::killTimer(int id)
{
Q_D(QObject);
if (d->threadData->eventDispatcher)
d->threadData->eventDispatcher->unregisterTimer(id);
}
代码倒是挺短,只是负担一下子交给eventDispatcher了。
QAbstractEventDispatcher
我们以windows下的情况为例看看吧:
void QEventDispatcherWin32::registerTimer(int timerId, int interval, QObject *object)
{
Q_D(QEventDispatcherWin32);
register WinTimerInfo *t = new WinTimerInfo;
t->dispatcher = this;
t->timerId = timerId;
t->interval = interval;
t->obj = object;
t->inTimerEvent = false;
t->fastTimerId = 0;
if (d->internalHwnd)
d->registerTimer(t);
d->timerVec.append(t); // store in timer vector
d->timerDict.insert(t->timerId, t); // store timers in dict
}
bool QEventDispatcherWin32::unregisterTimer(int timerId)
{
Q_D(QEventDispatcherWin32);
WinTimerInfo *t = d->timerDict.value(timerId);
d->timerDict.remove(t->timerId);
d->timerVec.removeAll(t);
d->unregisterTimer(t);
return true;
}
进而,代码进入了 QEventDispatcherWin32Private
void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
{
Q_Q(QEventDispatcherWin32);
int ok = 0;
if (t->interval > 20 || !t->interval || !qtimeSetEvent) {
ok = 1;
if (!t->interval) // optimization for single-shot-zero-timer
QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId));
else
ok = SetTimer(internalHwnd, t->timerId, (uint) t->interval, 0);
} else {
ok = t->fastTimerId = qtimeSetEvent(t->interval, 1, qt_fast_timer_proc, (DWORD_PTR)t,
TIME_CALLBACK_FUNCTION | TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
if (ok == 0) { // fall back to normal timer if no more multimedia timers available
ok = SetTimer(internalHwnd, t->timerId, (uint) t->interval, 0);
}
}
}
void QEventDispatcherWin32Private::unregisterTimer(WinTimerInfo *t, bool closingDown)
{
// mark timer as unused
if (!QObjectPrivate::get(t->obj)->inThreadChangeEvent && !closingDown)
QAbstractEventDispatcherPrivate::releaseTimerId(t->timerId);
if (t->interval == 0) {
QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
} else if (t->fastTimerId != 0) {
qtimeKillEvent(t->fastTimerId);
QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
} else if (internalHwnd) {
KillTimer(internalHwnd, t->timerId);
}
delete t;
}
呵呵,这段挺复杂的:
非0的timer
我们先看看对正常的timer,系统如何通知程序定时事件的呢?熟悉Windows编程的应该对这个回调函数很熟悉吧(呵呵,我对windows编程不熟,说错了别怪我哈)
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
if (message == WM_TIMER) {
Q_ASSERT(d != 0);
d->sendTimerEvent(wp);
return 0;
...
}
然后,看看这个sendTimerEvent做了什么
void QEventDispatcherWin32Private::sendTimerEvent(int timerId)
{
WinTimerInfo *t = timerDict.value(timerId);
if (t && !t->inTimerEvent) {
// send event, but don't allow it to recurse
t->inTimerEvent = true;
QTimerEvent e(t->timerId);
QCoreApplication::sendEvent(t->obj, &e);
// timer could have been removed
t = timerDict.value(timerId);
if (t) {
t->inTimerEvent = false;
}
}
}
挺简单的,就是通过QCoreApplication的sendEvent函数派发了QTimerEvent事件,剩下的工作当然就是Qt的事件系统的任务了。
间隔为0的timer
我们前面说了,对于间隔为0的timer,并没有启用系统的定时器,而是直接派发了一个QZeroTimerEvent 事件。我们知道,它进入事件系统以后,将会被派发到event函数
bool QEventDispatcherWin32::event(QEvent *e)
{
Q_D(QEventDispatcherWin32);
if (e->type() == QEvent::ZeroTimerEvent) {
QZeroTimerEvent *zte = static_cast<QZeroTimerEvent*>(e);
WinTimerInfo *t = d->timerDict.value(zte->timerId());
if (t) {
t->inTimerEvent = true;
QTimerEvent te(zte->timerId());
QCoreApplication::sendEvent(t->obj, &te);
t = d->timerDict.value(zte->timerId());
if (t) {
if (t->interval == 0 && t->inTimerEvent) {
// post the next zero timer event as long as the timer was not restarted
QCoreApplication::postEvent(this, new QZeroTimerEvent(zte->timerId()));
}
t->inTimerEvent = false;
}
}
return true;
} else if (e->type() == QEvent::Timer) {
QTimerEvent *te = static_cast<QTimerEvent*>(e);
d->sendTimerEvent(te->timerId());
}
return QAbstractEventDispatcher::event(e);
}
看到对QZeroTimerEvent进行什么处理了吧?
- 通过 QCoreApplication 的 sendEvent 派发出 QTimerEvent 事件
- 同时 产生一个新的 QZeroTimerEvent 事件,放入事件队列中
分享到:
相关推荐
总结,`QTimer`是QT中实现定时任务的关键工具,其灵活性和易用性使得它在各种场景下都能发挥重要作用。了解并熟练掌握`QTimer`的使用,能够极大地提升QT应用程序的功能性和用户体验。通过上述讲解和示例,相信你已经...
本教程主要聚焦于如何利用QTimer这个强大的工具来实现图片的移动,这是一个基础但实用的技能,能够帮助开发者更好地理解QT资源文件的管理和定时器的运用。 首先,我们需要了解QT资源系统。QT资源系统允许我们在应用...
在Qt中,可以通过设置QTimer的timeout()信号来触发自定义的处理函数,然后记录并分析这些函数被调用的实际时间,以评估QTimer的精度。 相比之下,Windows多媒体计时器是一种低级的定时服务,旨在提供比标准系统...
在Qt框架下,QTimer和QLabel是两个非常基础且重要的组件,它们可以结合使用来实现各种动态效果,包括本文提到的滚动字幕。QTimer是Qt中的计时器类,用于触发周期性的事件,而QLabel则是一个用于显示文本、图像或URL...
QTimer是Qt库中的一个关键组件,用于在应用程序中实现定时任务。在“QT软件学习记录”中,我们可以深入探讨QTimer的使用方法及其在Qt编程中的重要性。QTimer是一个信号-槽机制驱动的定时器,它可以用来触发事件、...
在Qt库中,QTimer是一个非常重要的类,用于实现定时器功能。在多线程编程中,有时我们需要在子线程中使用定时器来执行某些任务,以避免阻塞主线程,提高程序的响应性和效率。`QTimer`在子线程中的使用涉及到Qt的线程...
无论是在Windows还是Linux平台上,Qtimer都能很好地工作,为开发者提供了一致的API和跨平台的兼容性。在Windows系统中,Qtimer基于Windows的消息循环来实现定时功能;而在Linux系统中,它则利用了POSIX的定时器或者...
这个"Windows平台下,实现的QT实验源码.zip"压缩包包含了一系列基于Qt的实践项目,旨在帮助学习者理解和掌握Qt的基本用法。以下是这些实验项目的主要知识点: 实验一:拖动滑块,数字改变;改变数字,滑块拖动 这...
QTimer是Qt库中的一个关键组件,用于实现定时器功能,常用于在一定时间间隔后执行特定任务。在本教程中,我们将深入探讨如何在QT版本中有效使用QTimer。 首先,我们需要了解QTimer的基本概念。QTimer是一个信号/槽...
我们将使用Qt5和Qt Creator作为开发工具,因为这是在Windows 10环境下用mingw32编译器通过的配置。 首先,我们需要了解Qt中的时间管理和事件处理机制。在Qt中,我们可以使用`QTimer`类来实现定时任务。`QTimer`是...
* 需要在构造函数中创建QTimer对象,并设置其timerType为Qt::PreciseTimer以确保精度。 * 使用start()函数启动定时器,参数为间隔时间(毫秒)。 * 使用connect()函数将定时器的timeout()信号与槽函数连接,以便在...
在本文中,我们将深入探讨如何使用C++与Qt框架在Windows环境下实现一个进程监控程序,类似于Windows的任务管理器。这个程序能够实时监测并显示进程的内存占用和CPU使用情况,同时还具备动态调整查询间隔、保存日志...
QTimer使用,Pyqt界面制作和打包pyinstaller -F GOODOK --noconsole pyinstaller specfile
在QT中,QTimer类是一个非常重要的组件,它提供了一个方便的方式来实现定时任务,比如倒计时功能。本项目"QT QTimer定时器显示系统时间倒计时功能"就是利用QTimer来实现一个动态显示系统时间的倒计时程序。 首先,...
在本实践案例中,我们将深入探讨如何在Qt环境中利用QTimer类来实现定时器功能。QTimer是Qt库中的一个核心组件,它提供了一种简单的方式来执行周期性的任务或者在特定时间间隔后触发某一事件。这个案例适用于C++...
本资源提供了一个C/C++实现的定时器程序,包含源码和demo,支持在Windows和Linux上跨平台运行,这为我们提供了深入理解定时器机制和跨平台编程的一个实践案例。 首先,C++定时器的实现方式多种多样,常见的有以下几...
c/c++用一个源文件,一个头文件实现的 优先队列定时器 源码,包含测试代码,可运行。 说明: 1、源码中包含三个文件qtimer.cpp、qtimer.h、test_main.cpp; 2、qtimer.cpp中会创建一个优先队列来管理定时任务,一个...
总的来说,这个简单的控制台程序展示了如何在QT环境下利用QCoreApplication处理控制台输入,以及如何通过QTimer实现定时检查。这是一个基础的示例,实际的控制台应用程序可能需要处理更复杂的输入逻辑和事件处理。...
在本文中,我们将深入探讨如何使用Qt 5.3在Windows平台上实现一个功能丰富的时钟应用。Qt是一个跨平台的应用程序开发框架,广泛用于创建GUI(图形用户界面)应用程序。让我们一起了解如何利用Qt 5.3的特性来构建一个...