Qt事件派发流程

QApplication和QWidget默认是不连接的,需要在.pro文件里面加上QT += widgets就可以了

QApplication是一个应用程序抽象类,负责应用程序消息派发管理
QWidget是窗口类,抽象一个区域,部件,窗口
QWidget的show方法是显示窗口以及其子类的部件等
如果要想一个控件在中显示,必须要挂在到widget上去,
通常在创建对象的时候会有直接选定的构造函数,或者有setParent方法,来挂载,只有这样,该控件才会在窗口中展示,和参与消息的派发,并且如果是new出来的对象,还可以通过父类来析构,

Layout布局,只要布局对象选定了父类,那么在布局上的窗口都会自动选定,不必再指定


消息分两类:第一类是硬件产生的例如鼠标点击,键盘按下
第二类是软件产生,例如鼠标进入窗口

QCoreApplication::postEvent和QCoreApplication::SendEvent最终调用了QCoreApplication::notify函数,然后由QCoreApplication::notify去派发消息,如果我们重载了notify,那么就获得了所有消息的处理权.

bool QCoreApplication::notify(QObject * receiver, QEvent * event)
接受这个应用程序的所有的消息

Sends event to receiver: receiver->event(event). Returns the value that is returned from the receiver's event handler. Note that this function is called for all events sent to any object in any thread.
发送消息给接受者,调用receiver->event(event),返回的值是接受者消息处理函数的返回,注意这个函数可以发送所有的消息到任意线程的任意控件(在多线程时,注意加锁)

For certain types of events (e.g. mouse and key events), the event will be propagated to the receiver's parent and so on up to the top-level object if the receiver is not interested in the event (i.e., it returns false).
对于一些消息,比如鼠标和键盘,如果接收者对消息不感兴趣(也就是说接收者的event函数返回false),那么这个消息会传送给它的父对象,一直到有对这个消息感兴趣的接收者或者一直到顶层对象

There are five different ways that events can be processed; reimplementing this virtual function is just one of them. All five approaches are listed below:
有五种方法处理这个消息,重写notify这个虚函数只是其中一种.五种方法从实现普遍性角度来排列如下:

    Reimplementing paintEvent(), mousePressEvent() and so on. This is the commonest, easiest, and least powerful way.
    重写paintEvent(), mousePressEvent()等等,这个是最普遍,最简单的,也是影响最小的方法. 

    Reimplementing this function. This is very powerful, providing complete control; but only one subclass can be active at a time.
    重写notify这个函数,这个影响力很大,提供完全的控制,但是一个子类只能使用一次

    Installing an event filter on QCoreApplication::instance(). Such an event filter is able to process all events for all widgets, so it's just as powerful as reimplementing notify(); furthermore, it's possible to have more than one application-global event filter. Global event filters even see mouse events for disabled widgets. Note that application event filters are only called for objects that live in the main thread.
    安装消息过滤器,如果给QCoreApplication::instance对象安装一个过滤器,那么这个过滤器可以处理所有窗口的所有消息,所以它和重载notify一样强大,而且,它还可以安装许多全局钩子,而重载notify只有一个,全局的过滤器甚至可以探测到disable窗口的鼠标消息.注意Application的消息过滤器只能给那些主线程的QT对象用

    Reimplementing QObject::event() (as QWidget does). If you do this you get Tab key presses, and you get to see the events before any widget-specific event filters.


    Installing an event filter on the object. Such an event filter gets all the events, including Tab and Shift+Tab key press events, as long as they do not change the focus widget.

QT事件传递流程1.png-131.9kB
事件的传播是在组件层次上的,而不是依靠类继承机制

事件处理的几种方法,自下往上的分别是
重写具体事件—–>重写event—–>重写eventFilter—->重写notify

  • 重写具体事件:例void mousePressEvent(QMouseEvent* e)事件

    每个窗口控件是有许多具体事件的,每个事件都可以被重写,
    重写事件中还调用`e->accept()`和`e->ignore()`
    accept()用来告诉Qt这个事件已经被处理完了,所以不会传播给他的父组件
    ignore()用来告诉Qt这个事件没有被处理完,Qt会从其父组件中寻找类外的接受者
    
  • 重写event

    每个窗口控件只有一个`bool event(QEvent* e)`,
    通过`e->type()`这个方法与QEvent的枚举`QEvent::MouseButtonPress`来确定组件接收的是什么事件,然后再进行具体事件的处理
    这个函数是有返回值的,bool类型的
    如果返回true,就说明这个事件已经处理完了,就不会在传播给他的父组件了
    如果返回false,就说明这个事件没有被处理完,会继续向他的父组件传播,
    
    在处理完想要处理的事件之后,要在调用父类的event,以确保这个控件的其他事件可以调用默认的处理函数
    
    我认为true可以类比accept, false可以类比ignore
    
    bool event(QEvent *e)
    {
        if (e->type() == QEvent::MouseButtonPress) {
            qDebug() << "event函数中,重写MouseButtonPress";
            return true;
        } else {
            /* 一定不要忘了这一步 */
            QPushButton::event(e);
        }
    }
  • 重写eventFilter
    Filters events if this object has been installed as an event filter for the watched object.
    过滤事件,如果这个观察目标安装了一个事件过滤器

    事件过滤器`bool QObject::eventFilter(QObject * watched, QEvent * event)`
    第一个参数obj就是触发事件的控件,第二个参数是触发的事件
    如果要使用这个控件过滤器,首先这个控件要安装过滤器
        void QObject::installEventFilter(QObject * filterObj)
        filterObj需要过滤信息的控件,
    
    通常需要过滤出消息的控件会被定义成私有成员,并且这个控件过滤器通常是由父组件安装的
    
    eventFilter的返回值是bool,
    如果返回true,说明该事件已经被过滤处理了,不会再继续传播
    如果返回flase,说明该事件没有被过滤处理,会继续执行控件的event()函数,继续往下传播
    如果在其中调用accept().说明这个事件已经被处理了,所以也不会继续传播
    如果在其中调用了ignore().说明这个事件没有被处理,但会直接去其父组件中传播,而不会在给组件中传播,更不会执行该组件的event()函数
    
    在处理完想要处理的事件之后,要在调用父类eventFilter的,以确保之前父类默认的过滤事件仍然可移植性
    
class MyWidget : public QWidget
{
public:
    MyWidget();
protected:  /* 重载eventFilter */
    bool eventFilter(QObject *obj, QEvent *ev);
private:
    QPushButton* button;
};

MyWidget::MyWidget()
{
    button = new QPushButton(this);
    button->installEventFilter(this);
}
bool MyWidget::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == button) {
        if (event->type() == QEvent::QEvent::MouseButtonPress) {
            qDebug() << "事件过滤器处理的MousePressEvent事件";
            return true;
        } else {
            return false;
        }
    } else {
        // pass the event on to the parent class
        return QWidget::eventFilter(obj, event);
    }
}
事件的传递是直接通过多态直接找到触发事件的组件,如果该组件因为某些原因无法处理该事件,才会向其父组件传播

所以就算给widget组件安装上过滤器,也只会过滤传播到widget的事件,而不会过滤直接传播到widget组件里面子组件的事件

如果给QCoreApplication::instance对象安装一个过滤器,那么这个过滤器可以处理所有窗口的所有消息,所以它和重载notify一样强大

但是如果给QApplication安装上过滤器,就可以过滤所有传给该窗口的事件

如果一个组件安装多个过滤器,那过滤器调用的顺序是怎么安排的
  • 重写notify

    使用方法,继承QApplication类,然后在重写notify
    bool QCoreApplication::notify(QObject * receiver, QEvent * event)
    两个参数,控件及参数

    使用方法大致与过滤器相同,但是执行顺序notify在过滤器之前执行

    返回ture说明这个事件已经被处理了
    返回false
    如果还想程序处理这个事件,就在调用父类的notify,还有其他不想处理的事件,也要调用父类的notify来默认分发事件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值