一、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. Python 扩展插件

    扩展插件 我下载的本版自带 pip下载工具 cmd-pip 下载插件 pip install HTMLParser 如果提示版本问题,更新PIP 别用开始里面的CMD 使用管理者权限 请注意差别 输入 ...

  2. QT数据类型

    typedef signed char        int8_t;typedef short              int16_t;typedef int                int3 ...

  3. Linux系统学习之字符处理

    管道 管道是一种使用非常频繁的通信机制,我们可以使用管道符"|"来连接进程,由管道连接起来订单进程可以自动运行,如同有一个数据流一样,所以管道表现为输入输出重定向的一种方法,它可以 ...

  4. Python基础(4)列表、元组、字符串、字典、集合、文件操作

    列表.元组操作 字符串操作 字典操作 集合操作 文件操作 字符编码与转码 详见:http://www.cnblogs.com/alex3714/articles/5717620.html 1.列表和元 ...

  5. Objective-C 基于Aspects

    JavaScriptCore没有禁,因为各种小程序都在用 网络下载文件没有禁

  6. OSGeo.OGR.Geometry

    #region 程序集 ogr_csharp.dll, v2.0.50727 // D:\KM行业需求\C++\gdal17_cSharp\ogr_csharp.dll #endregion usin ...

  7. IDEA注册jar包使用和常用插件

    IDEA注册jar包使用 点击获取下载地址或生成注册码 一.安装完成后,先不启动,首先如下图修改相关的地方. 二.启动IDEA,并且激活IDEA IDEA插件仓库 IntelliJ IDEA Plug ...

  8. 【强化学习】python 实现 q-learning 例一

    本文作者:hhh5460 本文地址:https://www.cnblogs.com/hhh5460/p/10134018.html 问题情境 -o---T# T 就是宝藏的位置, o 是探索者的位置 ...

  9. 开源数据同步神器——canal

    前言 如今大型的IT系统中,都会使用分布式的方式,同时会有非常多的中间件,如redis.消息队列.大数据存储等,但是实际核心的数据存储依然是存储在数据库,作为使用最广泛的数据库,如何将mysql的数据 ...

  10. 5分钟入门自动化测试——你应该学会的Postman用法(2)

    前言 之前的一篇文章<你应该学会的Postman用法>,主要介绍了postman的一些高级的用法,便于日常开发和调试使用,本文的基础是对postman的基本使用以及一些高级用法有一定的了解 ...