• Qt之事件处理机制


    思维导读

      

    一、事件简介

      QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发。QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期。

      常见的QT事件类型如下:

         键盘事件: 按键按下和松开

         鼠标事件: 鼠标移动,鼠标按键的按下和松开

         拖放事件: 用鼠标进行拖放

         滚轮事件: 鼠标滚轮滚动

         绘屏事件: 重绘屏幕的某些部分

         定时事件: 定时器到时

         焦点事件: 键盘焦点移动

         进入和离开事件: 鼠标移入widget之内,或是移出

         移动事件: widget的位置改变

         大小改变事件: widget的大小改变

         显示和隐藏事件: widget显示和隐藏

         窗口事件: 窗口是否为当前窗口

       QT将系统产生的消息转化为QT事件,QT事件被封装为对象,所有的QT事件均继承抽象类QEvent,用于描述程序内部或外部发生的动作,任意的QObject对象都具备处理QT事件的能力。

    wKioL1gZ-degD-AyAAB-9l0vK0g875.png

    二、QT事件产生

    (1)操作系统事件

      操作系统将获取的事件,比如鼠标按键,键盘按键等keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件, 放入系统的消息队列中,Qt事件循环的时候读取消息队列中的消息,转化为QEvent并被分发到相应的QWidget对象,相应的QWidget中的event(QEvent *)进行事件处理会对事件进行处理,event(QEvent *)会根据事件类型调用不同的事件处理函数,在事件处理函数中发送QT预定义的信号,最终调用信号关联的槽函数。

      GUI应用程序的事件处理:

      A、QT事件产生后会被立即发送到相应的QWidget对象

      B、相应的QWidget中的event(QEvent *)进行事件处理

      C、event(QEvent *)根据事件类型调用不同的事件处理函数

      D、在事件处理函数中发送QT预定义的信号

      E、调用信号关联的槽函数

    (2)Qt应用程序自己产生

      程序产生事件有两种方式, 一种是调用QApplication::postEvent(), 例如QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用 QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理;

      另一种方式是调用sendEvent()函数,事件不会放入队列, 而是直接被派发和处理, QWidget::repaint()函数用的就是阻塞型的。

         sendEvent()中事件对象的生命期由Qt程序管理,支持分配在栈上和堆上的事件对象;postEvent()中事件对象的生命期由Qt平台管理,只支持分配在堆上的事件对象,事件被处理后由Qt平台销毁。

    三、Qt事件处理

    (1)事件调度

      事件有两种调度方式,同步和异步。

      Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环,先处理Qt事件队列中的事件, 直至为空,再处理系统消息队列中的消息, 直至为空, 处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理。

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

    (2)事件通知、派发

      事件过滤器是Qt中一个独特的事件处理机制, 功能强大而且使用起来灵活方便。通过事件过滤器, 可以让一个对象侦听拦截另外一个对象的事件。事件过滤器实现如下: 在所有Qt对象的基类QObject中有一个类型为QObjectList的成员变量,名字为eventFilters,当某个QObject(A)给另一个QObject(B)安装了事件过滤器后, B会把A的指针保存在eventFilters中。在B处理事件前,会先去检查eventFilters列表, 如果非空, 就先调用列表中对象的eventFilter()函数。一个对象可以给多个对象安装过滤器,一个对象能同时被安装多个过滤器, 在事件到达之后, 事件过滤器以安装次序的反序被调用。事件过滤器函数( eventFilter() ) 返回值是bool型, 如果返回true, 则表示事件已经被处理完毕, Qt将直接返回, 进行下一事件的处理。如果返回false, 事件将接着被送往剩下的事件过滤器或是目标对象进行处理。

      QT中,事件的派发是从 QApplication::notify()开始的, 因为QAppliction也是继承自QObject, 所以先检查QAppliation对象, 如果有事件过滤器安装在qApp上, 先调用事件过滤器,接下来QApplication::notify() 会过滤或合并一些事件(比如失效widget的鼠标事件会被过滤掉, 而同一区域重复的绘图事件会被合并),事件被送到reciver::event()处理。

         在reciver::event()中, 先检查有无事件过滤器安装在reciever上。若有, 则调用之。然后根据QEvent的类型, 调用相应的特定事件处理函数。常见的事件都有特定事件处理函数, 比如:mousePressEvent(), focusOutEvent(),  resizeEvent(), paintEvent(), resizeEvent()等等。在实际应用中, 经常需要重载特定事件处理函数处理事件。对于不常见的事件, 没有相对应的特定事件处理函数,如果要处理这些事件, 就需要使用别的办法, 比如重载event() 函数, 或是安装事件过滤器。

    (3)事件的转发

      对于某些类别的事件,如果在整个事件的派发过程结束后还没有被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口。Qt中和事件相关的函数通过两种方式相互通信,一种是QApplication::notify(), QObject::eventFilter(), QObject::event()通过返回bool值来表示是否已处理;另一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识,只用于event()函数和特定事件处理函数之间的沟通,而且只有用在某些类别事件上是有意义的, 这些事件就是上面提到的那些会被转发的事件, 包括: 鼠标, 滚轮, 按键等事件。

    (4)事件的处理和过滤

      QT提供了五种不同级别的事件处理和过滤:

         A、重写特定事件处理函数.

         最常见的事件处理办法就是重写mousePressEvent(), keyPressEvent(), paintEvent() 等特定事件处理函数。

       B、重写event()函数.

         重写event()函数时, 需要调用父类的event()函数来处理不需要处理或是不清楚如何处理的事件。

         return QWidget::event(event);

         C、在Qt对象上安装事件过滤器

         安装事件过滤器有两个步骤: (假设要用A来监视过滤B的事件)

         首先调用B的installEventFilter( const QOject *obj ), 以A的指针作为参数,所有发往B的事件都将先由A的eventFilter()处理。然后, A要重写QObject::eventFilter()函数, 在eventFilter() 中对事件进行处理。

         D、给QAppliction对象安装事件过滤器

    如果给QApplication对象装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前eventFilter()。在QApplication::notify() 中, 是先调用qApp的过滤器, 再对事件进行分析, 以决定是否合并或丢弃。

         E、继承QApplication类,并重载notify()函数

         Qt是用QApplication::notify()函数来分发事件的,要在任何事件过滤器查看任何事件之前先得到这些事件,重写notify()函数是唯一的办法。通常来说事件过滤器更好用一些, 因为不需要去继承QApplication类,而且可以给QApplication对象安装任意个数的事件过滤器。

    四、自定义事件和eventFilter示例

    class DefineEvent : public QEvent
    {
    public:
        const static QEvent::Type DefineType = static_cast<QEvent::Type>(QEvent::User + 0xFF);
    
        explicit DefineEvent(QString data) : QEvent(DefineType)
        {
            m_data = data;
        }
    
        QString data() {return m_data;}
    
    private:
        QString m_data;
    };
    #ifndef WIDGET_H
    #define WIDGET_H
     
    #include <QWidget>
    #include <QLineEdit>
    #include "StringEvent.h"
    #include <QMouseEvent>
    #include <QDebug>
    #include <QApplication>
     
    class Widget : public QWidget
    {
        Q_OBJECT
        QLineEdit m_edit;
    public:
        Widget(QWidget *parent = 0): QWidget(parent), m_edit(this)
        {
            m_edit.installEventFilter(this);
        }
        bool event(QEvent* evt)
        {
            if( evt->type() == QMouseEvent::MouseButtonDblClick )
            {
                qDebug() << "event: Before sentEvent";
                StringEvent e("D.T.Software");
                QApplication::sendEvent(&m_edit, &e);
                qDebug() << "event: After sentEvent";
            }
            return QWidget::event(evt);
        }
     
        bool eventFilter(QObject* obj, QEvent* evt)
        {
            if( (obj == &m_edit) && (evt->type() == StringEvent::TYPE) )
            {
                StringEvent* se = dynamic_cast<StringEvent*>(evt);
                qDebug() << "Receive: " << se->data();
                m_edit.insert(se->data());
                return true;
            }
     
            return QWidget::eventFilter(obj, evt);
        }
     
        ~Widget()
        {
     
        }
    };
     
    #endif // WIDGET_H
    补充:自定义事件类型可以使用registerEventType
    QEvent::Type ImageLoadedEvent::evType(){
        if(s_evType == QEvent::None)
        {
            s_evType = (QEvent::Type)registerEventType();
        }
    
    
        return s_evType;
    }
    
    
  • 相关阅读:
    简单明了的带你理解springboot原理和三大核心注解
    Spring Boot(一):入门篇
    【Mysql优化】聚簇索引与非聚簇索引概念
    Mysql索引原理与优化
    Mysql全文索引的使用
    索引的优缺点,如何创建索引
    184 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 04 例:字符串与byte(即:字节)数组间的相互转换
    183 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 03 String常用方法(下)
    182 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 02 String常用方法(上)
    181 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 01 String常用方法简介
  • 原文地址:https://www.cnblogs.com/xiaobingqianrui/p/9547924.html
Copyright © 2020-2023  润新知