一、Qt事件##

Qt会将系统消息(如鼠标按键、键盘按键等)转化为Qt事件,Qt事件被封装为对象且定义该对象的类均继承自抽象类QEvent。

二、Qt事件的产生##

1.操作系统产生###

  • Spontaneous events(自发事件)

    从系统得到的消息,比如鼠标按键,键盘按键等,放入系统消息队列中。

2.QT应用程序程序产生###

  • Posted events

      由Qt或应用程序产生,放入Qt消息队列。
    static void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority);
  • Sent events

      由Qt或应用程序产生,不放入队列,直接被派发和处理。
    static bool sendEvent(QObject *receiver, QEvent *event);

注:两个函数都是接受一个 QObject * 和一个 QEvent * 作为参数。

前辈们说 sendEvent 的 event 可分配在 stack或者heep 上; postEvent 的 event 必须分配在 heep 上。

但我试的两个怎么都可以

代码如下:

    QPointF pos(10,10);
QMouseEvent* mEvnPress = new QMouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::postEvent(&label,mEvnPress);

例子###

比如考虑重绘事件处理函数 paintEvent(),3种事件都能使得该函数被调用:

当窗口被其他窗口覆盖后,再次重新显示时,系统将产生 spontaneous 事件来请求重绘。

当我们调用 update() 时,产生的是 Posted 事件。

当我们调用 repaint() 时,产生的是 Sent 事件。

三、Qt事件的调用##

当调用QApplication::exec()时,就进入了事件队列循环,不断地检测事件并调用事件。

  • 先处理Qt事件队列中的事件, 直至为空。
  • 再处理系统消息队列中的消息, 直至为空。
  • 在处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理。

而在调用QApplication::sendEvent的时候, 消息会立即被处理,是同步的。实际上QApplication::sendEvent()是通过调用QApplication::notify(), 直接进入了事件的派发和处理环节。

四、事件的派发与处理##

事实上,Qt 事件的调用最终都会追溯到QCoreApplication::notify()函数。这个函数的声明是:

virtual bool QCoreApplication::notify ( QObject * receiver, QEvent * event );

该函数会将event发送给receiver,也就是调用receiver->event(event),这个函数就实现了事件的派发,根据event的类型将调用不同的事件处理器mousePressEvent(), keyPressEvent(), paintEvent()等等。

注意,QCoreApplication::notify()这个函数为任意线程的任意对象的任意事件调用,因此,它不存在事件过滤器的线程的问题。不过我们并不推荐这么做,因为notify()函数只有一个,而事件过滤器要灵活得多。

五、事件的过滤##

事件在经过notify()调用的内部函数notify_helper()的源码部分的源码如下:

bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
// send to all application event filters
if (sendThroughApplicationEventFilters(receiver, event))
return true;
// send to all receiver event filters
if (sendThroughObjectEventFilters(receiver, event))
return true;
// deliver the event
return receiver->event(event);
}

事件在传递到对象之前(调用receiver->event()函数之前),要先能通过 Applicaton 和 receiver 安装的过滤器,那么过滤器是怎么安装的:

QObject(A)->installEventFilter(QObject(B));

首先QObject(A)中有一个类型为QObjectList的成员变量,名字为eventFilters

当某个QObject(A)安装了事件过滤器之后, 它会将QObject(B)的指针保存在eventFilters中,在事件到达QObject::event()函数之前,会先查看该对象的eventFilters列表, 如果非空, 就先调用列表中对象的eventFilter()函数.

过滤器的定义如下:

bool QObject::eventFilter ( QObject * watched, QEvent * event )

事件过滤器函数eventFilter()返回值是bool型

如果返回true, 则表示该事件已经被处理完毕, Qt将直接返回, 进行下一事件的处理

如果返回false, 事件将接着被送往剩下的事件过滤器或是目标对象进行处理

如果使用installEventFilter()函数给一个对象安装事件过滤器,那么该事件过滤器只对该对象有效,只有这个对象的事件需要先传递给事件过滤器的eventFilter()函数进行过滤,其它对象不受影响。

给 QCoreApplication(由于也是QObject 派生类,安装过滤器方式与前述相同)安装的过滤器属于全局的事件过滤器对程序中的每一个对象都有效,任何对象的事件都是先传给eventFilter()函数。

事件过滤器的好处在于事件过滤器在目标对象接收到事件之前进行处理,如果我们将事件过滤掉,目标对象根本不会见到这个事件。

六、事件的转发##

对于某些类别的事件, 如果在整个事件的派发过程结束后还没有被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口.

如何判断一个事件是否被处理了呢? (有两个层次)

  • QApplication::notify(), QObject::eventFilter(), QObject::event() 通过返回bool值来表示是否已处理. “真”表示已经处理, “假”表示事件需要继续传递。
  • 另一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识,accept表示事件被处理。

为清楚起见,贴一点Qt的源码(来自 QApplication::notify()):

    case QEvent::ToolTip:
case QEvent::WhatsThis:
case QEvent::QueryWhatsThis:
{
QWidget* w = static_cast<QWidget *>(receiver);
QHelpEvent *help = static_cast<QHelpEvent*>(e);
QPoint relpos = help->pos();
bool eventAccepted = help->isAccepted();
while (w) {
QHelpEvent he(help->type(), relpos, help->globalPos());
he.spont = e->spontaneous();
res = d->notify_helper(w, w == receiver ? help : &he);
e->spont = false;
eventAccepted = (w == receiver ? help : &he)->isAccepted();
if ((res && eventAccepted) || w->isWindow())
break; relpos += w->pos();
w = w->parentWidget();
}
help->setAccepted(eventAccepted);
}
break;

这儿显示了对 WhatsThis 事件的处理:先派发给 w,如果事件被accepted 或已经是顶级窗口,则停止;否则获取w的父对象,继续派发。

七、总结##

现在我们可以总结一下 Qt 的事件处理,实际上是有五个层次:

  • 1.重写paintEvent()、mousePressEvent()等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。
  • 2.重写event()函数。event()函数是所有对象的事件入口,QObject和QWidget中的实现,默认是把事件传递给特定的事件处理函数。
  • 3.在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。
  • 4.在QCoreApplication::instance()上面安装事件过滤器。该过滤器将过滤所有对象的所有事件,因此和notify()函数一样强大,但是它更灵活,因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题:只能用在主线程。
  • 5.重写QCoreApplication::notify()函数。这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。但是全局范围内只能有一个被使用(因为QCoreApplication是单例的)。

为了进一步了解这几个层次的事件处理方式的调用顺序,我们可以编写一个测试代码:

#include <qapplication.h>
#include <QMainWindow>
#include <QPushButton>
#include <custombutton.h>
class Label : public QWidget
{
public:
Label()
{
installEventFilter(this);
} bool eventFilter(QObject *watched, QEvent *event)
{
if (watched == this) {
if (event->type() == QEvent::MouseButtonPress) {
qDebug() << "eventFilter";
}
}
return false;
} protected:
void mousePressEvent(QMouseEvent *)
{
qDebug() << "mousePressEvent";
} bool event(QEvent *e)
{
if (e->type() == QEvent::MouseButtonPress) {
qDebug() << "event";
}
return QWidget::event(e);
}
}; class EventFilter : public QObject
{
public:
EventFilter(QObject *watched, QObject *parent = 0) :
QObject(parent),
m_watched(watched)
{
} bool eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_watched) {
if (event->type() == QEvent::MouseButtonPress) {
qDebug() << "QApplication::eventFilter";
}
}
return false;
} private:
QObject *m_watched;
}; int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Label label;
app.installEventFilter(new EventFilter(&label, &label));
QPointF pos(10,10);
QMouseEvent* mEvnPress = new QMouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::postEvent(&label,mEvnPress);
label.show();
return app.exec();
}

运行结果:



因此可以知道,全局事件过滤器被第一个调用,之后是该对象上面的事件过滤器,其次是event()函数,最后是特定的事件处理函数。

参考##

abluemooon的博客

豆子空间

Qt__事件处理机制的更多相关文章

  1. java 事件处理机制:按下上下左右键控制小球的运动

    /** * 加深对事件处理机制的理解 * 通过上下左右键来控制一个小球的位置 */package com.test3;import java.awt.*;import javax.swing.*;im ...

  2. Android事件处理机制

    包括监听和回调两种机制. 1. 基于监听的事件处理: 事件监听包含三类对象,事件源,事件,事件监听器.Android的事件处理机制是一种委派式(Delegation)事件处理方式:普通组件(事件源)将 ...

  3. Android的两种事件处理机制

    UI编程通常都会伴随事件处理,Android也不例外,它提供了两种方式的事件处理:基于回调的事件处理和基于监听器的事件处理. 对于基于监听器的事件处理而言,主要就是为Android界面组件绑定特定的事 ...

  4. Android的Touch事件处理机制

    Android的Touch事件处理机制比较复杂,特别是在考虑了多点触摸以及事件拦截之后. Android的Touch事件处理分3个层面:Activity层,ViewGroup层,View层. 首先说一 ...

  5. IOS事件处理机制(关于触发者和响应者的确认)

    事件处理机制 在iOS中发生触摸后,事件会加入到UIApplication事件队列(在这个系列关于iOS开发的第一篇文章中我们分析iOS程序原理的时候就说过程序运行后UIApplication会循环监 ...

  6. Java Swing事件处理机制

    Java Swing的事件处理机制 Swing GUI启动后,Java虚拟机就启动三个线程,分别为主线程,事件派发线程(也是事件处理线程)和系统工具包线程. 主线程 :负责创建并显示该程序的初始界面: ...

  7. Qt事件处理机制

    研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 先给出原文 ...

  8. core java 8~9(GUI & AWT事件处理机制)

    MODULE 8 GUIs--------------------------------GUI中的包: java.awt.*; javax.swing.*; java.awt.event.*; 要求 ...

  9. Android的事件处理机制详解(二)-----基于监听的事件处理机制

    基于监听的事件处理机制 前言: 我们开发的app更多的时候是需要与用户的交互----即对用户的操作进行响应 这就涉及到了android的事件处理机制; android给我们提供了两套功能强大的处理机制 ...

随机推荐

  1. select * 和select 所有字段的区别

    文章取自http://blog.csdn.net/u014305991/article/details/44964171 MySQL 5.1.37 表记录数41,547,002,即4000w行 使用远 ...

  2. Linux Kernel 4.21已更新:优化AMD 7nm Zen2架构

    导读 AMD 7nm Zen2处理器预计将于明年第一季推出,采用下一代7nm EPYC. Linux Kernel 4.21已经更新,以优化AMD 7nm EPYC Rome(罗马)处理器. AMD ...

  3. Android学习之多触点滑动

    知识点: 1.对矩阵:Matrix类熟悉,链接:https://blog.csdn.net/jdsjlzx/article/details/52741445 2.MotionEvent详解:https ...

  4. MySQL(五)汇总和分组数据

    一.汇总数据 工作中经常需要汇总数据而不是将它们全部检索出来(实际数据本身:返回实际数据是对时间和处理资源的浪费),这种类型的检索有以下特点: ①确定表中的行数(或者满足某个条件或包含某个特定值的行数 ...

  5. SkylineGlobe6.5遍历信息树节点方法

    //------------------- //searchGeometries function searchGeometries2(parentNode, callbackFunc) { SGWo ...

  6. [03-01] JSP自定义标签

    1.自定义标签的概念 目前我们在JSP中使用的标签都是HTML的标签,浏览器会自动解析运行,例如<form action=""></form>,这里的for ...

  7. 随机指定范围内N个不重复的数

    此为工具类,支持抽奖业务需求,具体实现见下方代码: package com.org.test; import java.util.ArrayList; import java.util.List; p ...

  8. 3.2《想成为黑客,不知道这些命令行可不行》(Learn Enough Command Line to Be Dangerous)——检查文件开始与结尾

    检查文件两个互补的命令是head 和tail, 它们分别用于查看文件的开始(头部)和结束(尾部).head命令展示了文件的前10行.(Listing 11). ##Listing 11: 查看示例文件 ...

  9. JavaFx之不通过全局静态变量进行窗体通信

    百度了n多窗体通信,,,总是通过定义全局静态变量进行传值通信..我个人不喜欢一个controller里写满所有的布局(这样显得臃肿,但是组件传值方便).有没有另外的办法进行模块化并且可以传值呢.. 肯 ...

  10. [Spark][Hive][Python][SQL]Spark 读取Hive表的小例子

    [Spark][Hive][Python][SQL]Spark 读取Hive表的小例子$ cat customers.txt 1 Ali us 2 Bsb ca 3 Carls mx $ hive h ...