什么是自发事件?哪些类型的事件可以被propagated 或compressed? posting and sending 事件之间有何不同?什么时候应该调用 accept() 或是ignore() ? 如果这些问题你还不是很了解,那么继续看下去。
事件起源:
基于事件如何被产生与分发,可以把事件分为三类:
* Spontaneous 事件,由窗口系统产生,它们被放到系统队列中,通过事件循环逐个处理。
* Posted 事件,由Qt或是应用程序产生,它们被Qt组成队列,再通过事件循环处理。
* Sent 事件,由Qt或是应用程序产生,但它们被直接发送到目标对象。
当我们在main()函数的末尾调用QApplication::exec()时,程序进入了Qt的事件循环,大概来讲,事件循环如下面所示:
while (!exit_was_called)
{
while(!posted_event_queue_is_empty)
{
process_next_posted_event();
}
while(!spontaneous_event_queue_is_empty)
{
process_next_spontaneous_event();
}
while(!posted_event_queue_is_empty)
{
process_next_posted_event();
}
}
首先,事件循环处理所有的posted事件,直到队列空。然后再处理所有的spontaneous事件,最后它处理所有的因为处理spontaneous事件而产生的posted事件。send 事件并不在事件循环内处理,它们都直接被发送到了目标对象。现在看一下实践中的paint 事件是如何工作的。当一个widget第一次可见,或是被遮挡后再次变为可见,
窗口系统产生一个(spontaneous) paint事件,要求程序重画widget,事件循环最终从事件队列中捡选这个事件并把它分发到那个需要重画的widget。
并不是所有的paint事件都是由窗口系统产生的。当你调用QWidget::update()去强行重画widget,这个widget会post 一个paint 事件给自己。这个paint事件被放入队列,最终被事件循环分发之。
假如你很不耐烦,等不及事件循环去重画一个widget, 理论上,你应该直接调用paintEvent()强制进行立即的重画。但实际上这不总是可行的,因为paintEvent()函数是protected的(很可能访问不了)。它也绕开了任何存在的事件过滤器。因为这些原因,Qt提供了一个机制,直接sending事件而不是posting 。
QWidget::repaint()就使用了这个机制来强制进行立即重画。
posting 相对于sending的一个优势是,它给了Qt一个压缩(compress)事件的机会。假如你在一个widget上连续地调用update() 十次,因update()而产生的这十个事件,将会自动地被合并为一个单独的事件,但是QPaintEvents事件附带的区域信息也合并了。可压缩的事件类型包括:paint,move,resize,layout hint,language change。
最后要注意,你可以在任何时候调用QApplication::sendPostedEvent(),强制Qt产生一个对象的posted事件。
人工合成的事件
QT应用程序可以产生他们自己的事件,或是预定义类型,或是自定义类型。 这可以通过创建QEvent类或它的
子类的实例,并且调用QApplication:postEvent()或QApplication::sendEvent()来实现。
这两个函数需要一个 QObject* 与一个QEvent * 作为参数,假如你调用postEvent(),你必须用 new 操作符来创建事件对象,Qt会它被处理后帮你删除它。假如你用sendEvent(), 你应该在栈上来创建事件。下面举两个例子:
一是posting 事件:
QApplication::postEvent(mainWin, new QKeyEvent(QEvent::KeyPress,Key_X,'X',0));
二是sending 事件:
QKeyEventevent(QEvent::KeyPress, Key_X, 'X', 0);
QApplication::sendEvent(mainWin, &event);
Qt应用程序很少直接调用postEvent()或是sendEvnet(),因为大多数事件会在必要时被Qt或是窗口系统自动产生
。在大多数的情况下,当你想发送一个事件时,Qt已经为了准备好了一个更高级的函数来为你服务。(例如
update()与repaint())。
定制事件类型
qt允许你创建自己的事件类型,这在多线程的程序中尤其有用。在单线程的程序也相当有用,它可以作为
对象间的一种通讯机制。为什么你应该用事件而不是其他的标准函数调用,或信号、槽的主要原因是:事件既可用于同步也可用于异步(依赖于你是调用sendEvent()或是postEvents()),函数调用或是槽调用总是同步的。事件的另外一个好处是它可以被过滤。
演示如何post一个定制事件的代码片段:
constQEvent::Type MyEvent = (QEvent::Type)1234;
...
QApplication::postEvent(obj, new QCustomEvent(MyEvent));
事件必须是QCustomEvent类型(或子类)的。构造函数的参数是事件的类型,1024以下被Qt保留。其他可被程序使用。为处理定制事件类型,要重新实现customEvent()函数:
void MyLineEdit::customEvent(QCustomEvent*event)
{
if (event->type() == MyEvent) {
myEvent();
} else {
QLineEdit::customEvent(event);
}
}
QcustomEvent类有一个void *的成员,可用于特定的目的。你也可以子类化QCustomEvent,加上别的成员,但是你也需要在customEvent()中转换QCustomeEvent到你特有的类型。
事件处理与过滤
Qt中的事件可以在五个不同的层次上被处理
1,重新实现一个特定的事件handler
QObject与QWidget提供了许多特定的事件handlers,分别对应于不同的事件类型。(如paintEvent()对应paint事件)
2,重新实现QObject::event()
event()函数是所有对象事件的入口,QObject和QWidget中缺省的实现是简单地把事件推入特定的事件handlers。
3,在QObject安装上事件过滤器
事件过滤器是一个对象,它接收别的对象的事件,在这些事件到达指定目标之间。
4,在aApp上安装一个事件过滤器,它会监视程序中发送到所有对象的所有事件
5,重新实现QApplication:notify(),Qt的事件循环与sendEvent()调用这个函数来分发事件,通过重写它,你可以在别人之前看到事件。
一些事件类型可以被传递。这意味着假如目标对象不处理一个事件,Qt会试着寻找另外的事件接收者。用新的目标来调用QApplication::notify()。举例来讲,key事件是传递的,假如拥有焦点的Widget不处理特定键,Qt会分发相同的事件给父widget,然后是父亲的父亲,直到最顶层widget。
接受或是忽略?
可被传递的事件有一个accept()函数和一个ignore()函数,你可以用它们来告诉Qt,你“接收”或是
“忽略”这个事件。假如事件handler调用accept(),这个事件将不会再被传递。假如事件handler调用
ignore(),Qt会试着查找另外的事件接收者。
像大多数的开发者一样,你可能不会被调用accept()或是ignore()所烦恼。缺省情况下是“接收”,在
QWidget中的缺省实现是调用ignore(),假如你希望接收事件,你需要做的是重新实现事件handler,避免
调用QWidget的实现。假如你想“忽略”事件,只需简单地传递它到QWidget的实现。下面的代码演示了这一点:
void MyFancyWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Key_Escape) {
doEscape();
} else {
QWidget::keyPressEvent(event);
}
}
在上面的例子里,假如用户按了"ESC"键,我们会调用doEscape()并且事件被“接收”了(这是缺省的情况),
事件不会被传递到父widget,假如用户按了别的键,则调用QWidget的缺省实现。
voidQWidget::keyPressEvent(QKeyEvent *event)
{
event->ignore();
}
应该感谢ignore(),事件会被传递到父widget中去。
讨论到目前为至,我们都假设基类是QWidget,然而,同样的规则也可以应用到别的层次中,只要用QWidget
代替基类即可。举例来说:
void MyFancyLineEdit::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Key_SysReq) {
doSystemRequest();
} else {
QLineEdit::keyPressEvent(event);
}
}
由于某些原因,你会在event()中处理事件,而不是在特定的handler中,如keyPressEvent(),这个过程会有些不同。event() 会返回一个布尔值,来告诉调用者是否事件被accept或ignore,(true表示accept),从event()中调用accept()或是 ignore()是没有意义的。“Accept”标记是event()与特定事件handler之间的一种通讯机制。而从event()返回的布尔值却是用来与QApplication:notify()通讯的。在QWidgetk中缺省的event()实现是转换“Accept”标记为一个布尔值,如下所示:
boolQWidget::event(QEvent *event)
{
switch (e->type()) {
caseQEvent::KeyPress:
keyPressEvent((QKeyEvent *)event);
if (!((QKeyEvent *)event)->isAccepted())
returnfalse;
break;
caseQEvent::KeyRelease:
keyReleaseEvent((QKeyEvent *)event);
if (!((QKeyEvent *)event)->isAccepted())
returnfalse;
break;
...
}
returntrue;
}
到现在为至,我们所说的内容不仅仅适用于key事件,也适用于mouse,wheel,tablet,context menu等事件
Close事件有点不同,调用QCloseEvent:ignore()取消了关闭操作,而accept()告诉Qt继续执行正常的关闭操作。为了避免混乱,最好是在closeEvent()的新实现中明确地进行accept()与ignore()的调用:
void MainWindow::closeEvent(QCloseEvent *event)
{
if (userReallyWantsToQuit()) {
event->accept();
} else {
event->ignore();
}
}vv
分享到:
相关推荐
适用于libuv的Qt Event Dispatcher 事件调度程序将libuv添加为Qt事件循环的受支持后端。 当前,此存储库中省略了构建脚本,因为它们当前作为子模块包含在,并共享其构建设置。 请参阅qtjs-generator中的tests/...
实际上a.exec()便是Qt程序进入事件消息循环, 9.1.2 图形界面应用程序的消息处理模型 回调、os的魔抓windows、linux,从用户层到 内核层,如何管理进程、线程、 Os如何处理、底层机制 特点: 基于操作系统...
qt_eventdispatcher_libevent 是基于 Libevent 的 Qt 事件调度器 特点 非常快速 ... 支持Qt4和Qt5 ... 不需要Qt的私有头文件 ... 通过Qt4 和 Qt5 的事件调度,事件循环,定时器和socket通知测试
一个用qt实现的图片自动循环播放的小程序。 对刚刚学qt的新生有用!
Qt线程池,实现多个任务抢占多线程调度功能,用Qt事件循环解决假死
自己写 的qt在label上循环显示一组图片,语句提供多种分辨率的图片
qt_eventdispatcher_libev 是基于 libev 的 Qt 事件调度器。 特点 非常快速 ... 支持 Qt4 和 Qt5 ... 不需要 Qt 的私有头文件 ... 通过了 Qt4 和 Qt5 的事件调度,事件循环,定时器和 socket 通知测试
Qt实现字幕从右到左的循环滚动,可以控制字幕的滚动速度
qt 多线程 防止主线程做循环操作导致界面假死。试过多线程的几种方法,只有这个方法可行。代码亲测可行。在子线程死循环,界面正常不死!!!
实现循环队列,解决内存问题
qt下图片循环显示的几个很好的例子,包括源码
整个库本身经过精心设计,可与Qt事件循环完美配合,因此非常适合您的Qt项目。 我亲自创建了这个项目以提取.7z归档文件,以创建一个库,该库可以更新通过Qt Installer框架安装的Qt Apps,而无需使用Qt提供的更新...
通过Quick qml自带list实现类似京东电商移动app自动循环滚动图展示效果。也就是轮播图,可自动循环滚动,也可手动滑动循环展示。
Qt多线程循环打印数字0~9,知道按下“停止按钮”终止所有线程。
一个视频可以循环播放,多个视频也可以在列表中按照顺序自动播放,由此可以拓展,按钮点击,通过信号槽机制来实现视频播放。还可以设置视频播放窗口的大小等
Qt 程序需要在main()函数创建一个...这个函数就是开始 Qt 的事件循环。在执行 exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于
qt 实现xml的完整操作 遍历属性等 非常实用 项目中多次用
用QT widget 实现SDL VIDEO播放功能。
qt_eventdispatcher_libev 是基于 libev 的 Qt 事件调度器。 libev是高性能事件循环/事件模型的网络库,并且包含大量新特性。
Qode是Node.js的一个经过轻微修改的分支,它允许Node的事件循环与Qt或任何其他Gui事件循环合并。 它旨在与@nodegui/nodegui一起使用。 Qode通过允许通过NodeJS插件进行消息循环注入来实现此目的。 v2.0中的更改 在...