一、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. 解决普通用户登录ulimit 报错问题

    [root@master1 ~]# su - fengjian-bash: ulimit: open files: cannot modify limit: Operation not permitt ...

  2. YOLO2 (2) 测试自己的数据

    Windos10 linux同样过程 现有问题: 本文过程在linux下类似,可以正常通过.windons下,运行python脚本出现问题,无法正常输出0001.txt(标定文件),所以只能使用lin ...

  3. linux shell脚本调用java main方法 代码传值

    #!/bin/bash #description: ljdjService export PRG_HOME=/ainmc/work/toptea/dataTransfer PRG_KEYWORD=pr ...

  4. 编写简单Linux内核模块

    模块代码如下 //main.c #include <linux/kernel.h> #include <linux/module.h> #include <linux/i ...

  5. PAT A1029 Median (25 分)——队列

    Given an increasing sequence S of N integers, the median is the number at the middle position. For e ...

  6. C++ 指针常量和常量指针

    1.指针常量(*const):对应指针变量,即指针本身是常量,指针指向的内容可以被修改. 2.常量指针(const*):常量的指针,即指针指向的内容不能被修改,但指针本身是变量,可以被修改.

  7. ubuntu install pip

    ubuntu 安装pip sudo apt-get update sudo apt-get upgrade sudo apt-get install python-pip

  8. Luogu P2661 [NOIP2015] 信息传递

    qwq 今天做完并查集突然想起来这道以前做的好(shui)题, 虽然是黄题,但是是并查集一个比较特别的用法 这道题大概可以用求最小环的方式来做,但是从直觉上果然还是并查集w 乍一看只要求出“父→子”即 ...

  9. REST-framework快速构建API--生成Swagger接口文档

    一.Swagger概述 1.引言 当接口开发完成,紧接着需要编写接口文档.传统的接口文档使用Word编写,or一些接口文档管理平台进行编写,但此类接口文档维护更新比较麻烦,每次接口有变更,需要手动修改 ...

  10. Flutter - 自动生成Android & iOS图标

    对于要发布的app来说,做图标是一个麻烦的事,你需要知道N个图标的分辨率,然后用PhotoShop一个个修改导出. PS好图标之后,按照各自的位置放进去. ********************** ...