• 事件总结-常用总结


    Qt 的事件是整个 Qt 框架的核心机制之一,也比较复杂。说它复杂,更多是因为它涉及到的函数众多,而处理方法也很多,有时候让人难以选择。现在我们简单总结一下 Qt 中的事件机制。

    Qt 中有很多种事件:鼠标事件、键盘事件、大小改变的事件、位置移动的事件等等。如何处理这些事件,实际有两种选择:

    1. 所有事件对应一个事件处理函数,在这个事件处理函数中用一个很大的分支语句进行选择,其代表作就是 win32 API 的WndProc()函数:
    1. LRESULT CALLBACK WndProc(HWND hWnd,
    2. UINT message,
    3. WPARAM wParam,
    4. LPARAM lParam)

    在这个函数中,我们需要使用switch语句,选择message参数的类型进行处理,典型代码是:

    1. switch(message)
    2. {
    3. case WM_PAINT:
    4. // ...
    5. break;
    6. case WM_DESTROY:
    7. // ...
    8. break;
    9. ...
    10. }
    1. 每一种事件对应一个事件处理函数。Qt 就是使用的这么一种机制:
      • mouseEvent()
      • keyPressEvent()

    Qt 具有这么多种事件处理函数,肯定有一个地方对其进行分发,否则,Qt 怎么知道哪一种事件调用哪一个事件处理函数呢?这个分发的函数,就是event()。显然,当QMouseEvent产生之后,event()函数将其分发给mouseEvent()事件处理器进行处理。

    event()函数会有两个问题:

    1. event()函数是一个 protected 的函数,这意味着我们要想重写event(),必须继承一个已有的类。试想,我的程序根本不想要鼠标事件,程序中所有组件都不允许处理鼠标事件,是不是我得继承所有组件,一一重写其event()函数?protected 函数带来的另外一个问题是,如果我基于第三方库进行开发,而对方没有提供源代码,只有一个链接库,其它都是封装好的。我怎么去继承这种库中的组件呢?
    2. event()函数的确有一定的控制,不过有时候我的需求更严格一些:我希望那些组件根本看不到这种事件。event()函数虽然可以拦截,但其实也是接收到了QMouseEvent对象。我连让它收都收不到。这样做的好处是,模拟一种系统根本没有那个事件的效果,所以其它组件根本不会收到这个事件,也就无需修改自己的事件处理函数。这种需求怎么办呢?

    这两个问题是event()函数无法处理的。于是,Qt 提供了另外一种解决方案:事件过滤器。事件过滤器给我们一种能力,让我们能够完全移除某种事件。事件过滤器可以安装到任意QObject类型上面,并且可以安装多个。如果要实现全局的事件过滤器,则可以安装到QApplication或者QCoreApplication上面。这里需要注意的是,如果使用installEventFilter()函数给一个对象安装事件过滤器,那么该事件过滤器只对该对象有效,只有这个对象的事件需要先传递给事件过滤器的eventFilter()函数进行过滤,其它对象不受影响。如果给QApplication对象安装事件过滤器,那么该过滤器对程序中的每一个对象都有效,任何对象的事件都是先传给eventFilter()函数。

    事件过滤器可以解决刚刚我们提出的event()函数的两点不足:首先,事件过滤器不是 protected 的,因此我们可以向任何QObject子类安装事件过滤器;其次,事件过滤器在目标对象接收到事件之前进行处理,如果我们将事件过滤掉,目标对象根本不会见到这个事件。

    事实上,还有一种方法,我们没有介绍。Qt 事件的调用最终都会追溯到QCoreApplication::notify()函数,因此,最大的控制权实际上是重写QCoreApplication::notify()。这个函数的声明是:

    virtual bool QCoreApplication::notify ( QObject * receiver, QEvent * event );
    

    该函数会将event发送给receiver,也就是调用receiver->event(event),其返回值就是来自receiver的事件处理器。注意,这个函数为任意线程的任意对象的任意事件调用,因此,它不存在事件过滤器的线程的问题。不过我们并不推荐这么做,因为notify()函数只有一个,而事件过滤器要灵活得多。

    现在我们可以总结一下 Qt 的事件处理,实际上是有五个层次:

    1. 重写paintEvent()mousePressEvent()等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。
    2. 重写event()函数。event()函数是所有对象的事件入口,QObjectQWidget中的实现,默认是把事件传递给特定的事件处理函数。
    3. 在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。
    4. QCoreApplication::instance()上面安装事件过滤器。该过滤器将过滤所有对象的所有事件,因此和notify()函数一样强大,但是它更灵活,因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题:只能用在主线程。
    5. 重写QCoreApplication::notify()函数。这是最强大的,和全局事件过滤器一样提供完全控制,并且不受线程的限制。但是全局范围内只能有一个被使用(因为QCoreApplication是单例的)。

    为了进一步了解这几个层次的事件处理方式的调用顺序,我们可以编写一个测试代码:

    1. class Label : public QWidget
    2. {
    3. public:
    4. Label()
    5. {
    6. installEventFilter(this);
    7. }
    8. bool eventFilter(QObject *watched, QEvent *event)
    9. {
    10. if (watched == this) {
    11. if (event->type() == QEvent::MouseButtonPress) {
    12. qDebug() << "eventFilter";
    13. }
    14. }
    15. return false;
    16. }
    17. protected:
    18. void mousePressEvent(QMouseEvent *)
    19. {
    20. qDebug() << "mousePressEvent";
    21. }
    22. bool event(QEvent *e)
    23. {
    24. if (e->type() == QEvent::MouseButtonPress) {
    25. qDebug() << "event";
    26. }
    27. return QWidget::event(e);
    28. }
    29. };
    30. class EventFilter : public QObject
    31. {
    32. public:
    33. EventFilter(QObject *watched, QObject *parent = 0) :
    34. QObject(parent),
    35. m_watched(watched)
    36. {
    37. }
    38. bool eventFilter(QObject *watched, QEvent *event)
    39. {
    40. if (watched == m_watched) {
    41. if (event->type() == QEvent::MouseButtonPress) {
    42. qDebug() << "QApplication::eventFilter";
    43. }
    44. }
    45. return false;
    46. }
    47. private:
    48. QObject *m_watched;
    49. };
    50. int main(int argc, char *argv[])
    51. {
    52. QApplication app(argc, argv);
    53. Label label;
    54. app.installEventFilter(new EventFilter(&label, &label));
    55. label.show();
    56. return app.exec();
    57. }

    我们可以看到,鼠标点击之后的输出结果是:

    1. QApplication::eventFilter
    2. eventFilter
    3. event
    4. mousePressEvent

    因此可以知道,全局事件过滤器被第一个调用,之后是该对象上面的事件过滤器,其次是event()函数,最后是特定的事件处理函数。

  • 相关阅读:
    uniapp 之使用 u-upload 组件来实现图片上传
    元宇宙在技术大爆炸时代迎来链游新世界
    【Python实战】WIFI密码小工具,甩万能钥匙十条街,WIFI任意连哦~(附源码)
    【侯捷C++-----STL与泛型编程】
    宇视网络视频录像机人机实况画面偏色排查步骤
    Rancher(V2.6.3)安装K8s教程
    For循环控制
    如何分析Activity被relaunch原因
    opencv阈值图像Threshold方法
    arm 内核版本编译记录
  • 原文地址:https://blog.csdn.net/vcit102/article/details/125994906