• qt 消息(事件)机制


    1.QEventLoop (即Qt中的事件循环类)

    消息循环在QEventLoop类中实现。通过QEventLoop::exec()可以进入一个消息循环的阻塞状态中,也就是不断地PeekMessage、TranslateMessage、DispatchMessage(和windows 消息机制差不多的)。
    Application类中,除去启动参数、版本等相关东西后,关键就是维护了一个QEventLoop,Application的exec就是QEventLoop的exec。不过Application中的这个EventLoop,我们称作“主事件循环”Main EventLoop。所有的事件分发、事件处理都从这里开始。

    1. int main(int argc, char *argv[])
    2. {
    3. QCoreApplication app(argc, argv);
    4. //或者QGuiApplication, 或者 QApplication
    5. ...
    6. ...
    7. return app.exec();
    8. }

     在windows平台上,其底层原理也是通过PeekMessage、TranslateMessage、DispatchMessage从消息队列进行捕捉、转换、分发。

    2.QEvent

    QT将系统产生的消息转化为QT事件,QT事件被封装为对象,所有的QT事件均继承抽象类QEvent。
    比如键盘、鼠标产生的keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件(他们被封装成QMouseEvent和QKeyEvent)。其他的如QPaintEvent、QCloseEvent。

    每一个事件处理函数,都是带有参数的,这个参数是QEvent的子类,携带了各种事件的参数。比如按键事件 void keyPressEvent(QKeyEvent *event) 中的QKeyEvent, 就包括了按下的按键值key、 count等等。


    3.事件过滤器

    Qt还提供了事件过滤机制,在事件分发之前先过滤一部分事件

    a.安装一个事件过滤器.

    void QObject::installEventFilter(QObject *filterObj) 

    延伸:一个是移除对应的事件过滤器

    void QObject::removeEventFilter(QObject *obj)

    1. QObject* objA = new MyQObjectA;
    2. QObject* objB = new MyQObjectB;
    3. // 安装事件过滤器;
    4. objA->installEventFilter(objB);
    5. // 移除事件过滤器;
    6. objA->removeEventFilter(objB);
    7. 使用 installEventFilter方法 给对象objA安装objB的事件过滤器,这样objB对象的eventFilter方法中就可以接收到objA对象的所有事件了,如果objA对象不想objB对象再监听自己的事件了就使用 removeEventFilter方法移除objB对象对事件的监听。

    b.重写QObject类的eventFilter函数。
    virtual bool eventFilter(QObject *watched, QEvent *event);

    • 如果返回true,表示事件过滤,不会发送到对象本身。
    • 如果返回false,表示事件未过滤,会通过event()方法将事件分发到对象。
    • 返回给基类进行处理,例:return QObject::eventFilter(obj, event)。

    一般的操作办法:父窗口类通过重写eventFilter方法来监听子控件的相关事件进行处理

    好处就是:不需要通过重写控件的方式获取某些事件。

    1. Widget::Widget(QWidget *parent) :
    2. QWidget(parent),
    3. ui(new Ui::Widget)
    4. {
    5. ui->setupUi(this);
    6. ui->label->installEventFilter(this);
    7. }
    8. Widget::~Widget()
    9. {
    10. delete ui;
    11. }
    12. bool Widget::eventFilter(QObject *obj, QEvent *event)
    13. {
    14. if(obj == ui->label)
    15. {
    16. //鼠标进入的时候
    17. if (event->type() == QEvent::Enter)
    18. {
    19. ui->label->setText("我是红色");
    20. ui->label->setStyleSheet(redStyle);
    21. return true;
    22. }
    23. else if(event->type() == QEvent::Leave) //鼠标离开
    24. {
    25. ui->label->setText("我是黑色");
    26. ui->label->setStyleSheet(blackStyle);
    27. return true;
    28. }
    29. return false;//别的事件会传给label对象
    30. }
    31. // standard event processing
    32. return QWidget::eventFilter(obj, event);
    33. }

    4.event()函数主要用于事件的分发

    通过QObject类event函数处理
    virtual bool event(QEvent *event);
    event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。如果传入的事件已被识别并且处理,返回 true,否则返回 false。如果返回值是 true,QApplication 会认为这个事件已经处理完毕,会继续处理事件队列中的下一事件;如果返回值是 false,QApplication 会尝试寻找这个事件的下一个处理函数。

    1. //重写event函数
    2. bool MyWidget::event(QEvent *e)
    3. {
    4. //如果该对象触发的事件类型是键盘按下 将e转成键盘事件并判断是否是tab键 是则处理 否则重新分配给event的默认处理方式
    5. if (e->type() == QEvent::KeyPress) {
    6. QKeyEvent *keyEvent = static_cast<QKeyEvent*>(e);//强制类型转换
    7. if (keyEvent->key() == Qt::Key_Tab) {
    8. qDebug() << "You press tab.";
    9. return true;
    10. }
    11. }
    12. //重新分配给event的默认处理方式 //必要的,重新处理其他事件
    13. return QWidget::event(e);
    14. //若返回的不是再调用QWidget的event函数 而是直接返回false 则表示只能处理tab键 其它键都不能接收了
    15. //return false;
    16. }

     注意:其实eventFilter和event函数已经在窗口函数中执行的,相当于在窗口回调函数中进行二次处理(事件过滤或拦截)。

    5.事件处理

    在QWidget子类中,通过重写keyPressEvent、keyReleaseEvent等等事件处理函数,做一些自定义的事件处理。下面为常见的事件处理函数:

    1. virtual void mousePressEvent(QMouseEvent *event);
    2. virtual void mouseReleaseEvent(QMouseEvent *event);
    3. virtual void mouseDoubleClickEvent(QMouseEvent *event);
    4. virtual void mouseMoveEvent(QMouseEvent *event);
    5. #if QT_CONFIG(wheelevent)
    6. virtual void wheelEvent(QWheelEvent *event);
    7. #endif
    8. virtual void keyPressEvent(QKeyEvent *event);
    9. virtual void keyReleaseEvent(QKeyEvent *event);
    10. virtual void focusInEvent(QFocusEvent *event);
    11. virtual void focusOutEvent(QFocusEvent *event);
    12. virtual void enterEvent(QEvent *event);
    13. virtual void leaveEvent(QEvent *event);
    14. virtual void paintEvent(QPaintEvent *event);
    15. virtual void moveEvent(QMoveEvent *event);
    16. virtual void resizeEvent(QResizeEvent *event);
    17. virtual void closeEvent(QCloseEvent *event);
    18. #ifndef QT_NO_CONTEXTMENU
    19. virtual void contextMenuEvent(QContextMenuEvent *event);
    20. #endif
    21. #if QT_CONFIG(tabletevent)
    22. virtual void tabletEvent(QTabletEvent *event);
    23. #endif
    24. #ifndef QT_NO_ACTION
    25. virtual void actionEvent(QActionEvent *event);
    26. #endif
    27. #if QT_CONFIG(draganddrop)
    28. virtual void dragEnterEvent(QDragEnterEvent *event);
    29. virtual void dragMoveEvent(QDragMoveEvent *event);
    30. virtual void dragLeaveEvent(QDragLeaveEvent *event);
    31. virtual void dropEvent(QDropEvent *event);
    32. #endif
    33. virtual void showEvent(QShowEvent *event);
    34. virtual void hideEvent(QHideEvent *event);
    35. virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result);
    36. // Misc. protected functions
    37. virtual void changeEvent(QEvent *);

    延伸补充:

    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提供了五种不同级别的事件处理和过滤:

         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对象安装任意个数的事件过滤器。

  • 相关阅读:
    Apache Hive源码阅读环境搭建
    layui移除(删除)table表格的一行
    【C语言经典100题#4】判断三角形
    后端API接口性能优化的10种方案,真有用!
    在vim中使用perltidy美化perl代码
    论文:Cover letter,添加基金项目:山东省自然科学基金
    阿里 P9 用 500 多页手册完成双十一高并发秒杀系统,绝了
    数据结构--第七天
    Android Studio实现简单的页面跳转(简单教程)
    计算机毕业设计之java+ssm手机综合类门户网站
  • 原文地址:https://blog.csdn.net/baidu_16370559/article/details/128062334