当前位置:   article > 正文

qt 消息(事件)机制_qt消息机制

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/949528
推荐阅读
相关标签
  

闽ICP备14008679号