找回密码
 立即注册
收起左侧

Qt学习之路第22篇 事件总结

4
回复
18439
查看
[复制链接]
累计签到:3 天
连续签到:1 天
来源: 2013-9-9 14:08:43 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
版权声明

该文章原创于Qter开源社区(www.qter.org),作者devbean,博客www.devbean.net,转载请注明出处!


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. }
复制代码
2.每一种事件对应一个事件处理函数。Qt 就是使用的这么一种机制:

  • mouseEvent()
  • keyPressEvent()


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

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


  • event() 函数是一个 protected 的函数,这意味着我们要想重写 event(),必须继承一个已有的类。试想,我的程序根本不想要鼠标事件,程序中所有组件都不允许处理鼠标事件,是不是我得继承所有组件,一一重写其 event() 函数?protected 函数带来的另外一个问题是,如果我基于第三方库进行开发,而对方没有提供源代码,只有一个链接库,其它都是封装好的。我怎么去继承这种库中的组件呢?


  • event() 函数的确有一定的控制,不过有时候我的需求更严格一些:我希望那些组件根本看不到这种事件。event() 函数虽然可以拦截,但其实也是接收到了 QMouseEvent 对象。我连让它收都收不到。这样做的好处是,模拟一种系统根本没有那个事件的效果,所以其它组件根本不会收到这个事件,也就无需修改自己的事件处理函数。这种需求怎么办呢?


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


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


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

  1. virtual bool QCoreApplication::notify ( QObject * receiver, QEvent * event );
复制代码

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


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

  • 重写 paintEvent()、mousePressEvent() 等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。
  • 重写 event() 函数。event() 函数是所有对象的事件入口,QObject 和 QWidget 中的实现,默认是把事件传递给特定的事件处理函数。
  • 在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。
  • 在 QCoreApplication::instance() 上面安装事件过滤器。该过滤器将过滤所有对象的所有事件,因此和 notify() 函数一样强大,但是它更灵活,因为可以安装多个过滤器。全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。全局过滤器有一个问题:只能用在主线程。
  • 重写 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. }
复制代码

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


QApplication::eventFilter eventFilter event mousePressEvent

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








回复

使用道具 举报

累计签到:5 天
连续签到:1 天
2015-6-17 16:30:23 显示全部楼层
首先,事件过滤器不是 protected 的,因此我们可以向任何 QObject 子类安装事件过滤器
看到这篇和上篇反复提到protected

是protected 的话有影响吗??
回复 支持 反对

使用道具 举报

累计签到:6 天
连续签到:1 天
2015-9-9 16:53:31 显示全部楼层
虚客 发表于 2015-6-17 16:30
首先,事件过滤器不是 protected 的,因此我们可以向任何 QObject 子类安装事件过滤器
看到这篇和上篇反复 ...

protected是保护类型的,有访问权限的限制吧。
回复 支持 反对

使用道具 举报

累计签到:3 天
连续签到:1 天
2015-11-17 14:00:34 显示全部楼层
作者没说,如果过滤器是protected会怎样啊? 不然,新手感觉不到哪里有什么影响呀~
回复 支持 反对

使用道具 举报

累计签到:2 天
连续签到:1 天
2017-3-12 00:31:12 显示全部楼层
本帖最后由 叶文博 于 2017-3-12 00:32 编辑

protected 和public的区别 子类实例是否能   . or  -> 得到函数接口,public可以,而protected只有衍生类和自己类内能使用,实例化对象是看不到的,  但是没理解eventFilter 不是protected的利是?

测试了 楼主代码- -,  事件是先检测全局的eventFilter 然后是具体对象的1.evenFilter 2.event 3.event_handler   但是楼主代码的  全局的EventFilter qDebug()只有是EventFilter指针时才会输出 "QApplication::eventFilter";



回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了