一列Qt事件处理机制分析不到位导致的Bug

本文深入探讨了Qt的事件处理机制,包括五种事件处理方法及其应用场景,解析了Qt事件的传递与接收流程。文章通过一个具体的Bug实例,分析了在自定义QGraphicsView中处理mousePressEvent时,事件顺序对程序行为的影响,强调了正确处理事件传递的重要性。修复Bug的方法是确保先调用基类的事件处理函数,以保证事件的正常处理。
摘要由CSDN通过智能技术生成
一、Qt事件处理机制的知识总结
(1-1)五种Qt事件使用方法总结

【方法一】
重新实现部件的 paintEvent()、mousePressEvent() 等事件处理函数。这是最常用的一种方法(只能用来处理特定部件的特定事件)

【方法二】
重新实现 notify() 函数。这个函数功能强大,提供了完全的控制,可以在事件过滤器得到事件之前就获得它们。(一次只能处理一个事件)

【方法三】
向 QApplication 对象上安装事件过滤器。因为一个程序只有一个 QApplication 对象,所以这样实现的功能与使用 notify() 函数是相同的(可以同时处理多个事件)但是应用程序事件过滤器只对位于主线程中的对象调用。

【方法四】
重新实现 event() 函数。QObject 类的 event() 函数可以在事件到达默认的事件处理函数之前获得该事件。

【方法五】
在对象上安装事件过滤器。使用事件过滤器可以在一个界面类中同时处理不同子部件的不同事件。

在实际编程中,最常用的是方法一,其次是方法五。方法二需要继承自 QApplication 类;而方法三需要使用一个全局事件过滤器,这将减缓事件的传递,所以虽然方法二和方法三功能强大,但是很少被用到;方法四在开发中也较为常用,对于需要截取事件传递的操作可以使用重载改方法实现。

方法一使用起来较为方便;由于一个界面类中会有多个子部件,所以说方法五使用起来也较为方便。

(1-2)Qt事件的解析机制

​ 在每个程序的 main() 函数的最后都会调用 QApplication 类的 exec() 函数,它会使 Qt 应用程序进人事件循环,这样就可以使应用程序在运行时接收发生的各种事件并进行事件处理。一旦有事件发生,Qt 便会构建一个相应的 QEvent 子类对象来表示,然后将它传递给相应的 QObject 对象或其子对象。

(1-3)Qt事件的传递机制

​ 事件的事件传递顺序:先是事件过滤器,然后是焦点部件的 event() 函数,最后是焦点部件的事件处理函数,例如:键盘按下事件函数;如果焦点部件忽略了该事件,那么会执行父部件的事件处理函数,如下图所示。注意,event() 函数和事件处理函数,是在该部件内进行重新定义的,而事件过滤器却是在该部件的父部件中进行定义的。

在这里插入图片描述

Qt中事件的接收流程如下图所示,postEvent会将事件插入事件队列,从而能异步处理事件,sendEvent直接将事件发出进行处理。如果返回TRUE或者调用accept(),说明事件已经被处理完,不会再向父组件传播,如果返回false或者调用ignore(),则表明这个事件没有被处理完,Qt会将事件继续传递给其父组件继续处理该事件。
在这里插入图片描述

二、记录一列由Qt事件处理机制分析不到位导致的Bug
2-1、背景描述

​ 首先派生了一个自QGraphicsView父类的图形视图类,然后在该图形视图类下重写mousePressEvent事件处理函数。在该函数中,需要获取图形视图下的各个QGraphicsItem的data数据,然后判断某个QGraphicsItem是否被选择(isSelected()),从而发出对应的是否被选择信号,通知其他类组件。

​ 大致就是这样一个代码设计场景。

2-2、Bug记录

​ 在实际开发中,写下了以下的Bug代码:

void RotateView::mousePressEvent(QMouseEvent *event)
{
    QList<QGraphicsItem*> items = this->items();

    foreach (QGraphicsItem *item,items)
    {
		/* ... */
    }
    
    QGraphicsView::mousePressEvent(event);
}

​ 在以上代码中,重写mousePressEvent,对鼠标的按下事件进行处理,当对触摸屏进行触摸时,需要获取各个QGraphicsItem的参数进行处理,但是在实际调试过程中,发现各个QGraphicsItem的状态总是延后一次更新!!从而导致处理QGraphicsItem的程序流程出现异常现象。

​ 分析问题出现的原因,最终发现将 QGraphicsView::mousePressEvent(event);放在了mousePressEvent事件处理函数的末尾,从而导致在获取QGraphicsItem状态数据之前QGraphicsItem没有进行事件传递,使图形项更新少了一次,从而出现异常。

​ 针对问题,调整代码位置,如下代码片段:

void RotateView::mousePressEvent(QMouseEvent *event)
{
    QGraphicsView::mousePressEvent(event);
    
    QList<QGraphicsItem*> items = this->items();

    foreach (QGraphicsItem *item,items)
    {
		/* ... */
    }
}

​ 问题得以解决!

三、总结

​ (1)关于事件处理机制,需要确保事件的正确交付。

​ (2)对于以下函数:

1bool QObject::eventFilter(QObject *watched, QEvent *event)    
2bool QObject::event(QEvent *e)
3bool QCoreApplication::notify(QObject *receiver, QEvent *event)

​ 在进行事件接收和处理时,需要确保没有被处理的事件能继续上报传递。例如:

class MyClass : public QWidget
{
    Q_OBJECT
public:
	...
    bool event(QEvent* ev) override
    {
       ......
        // 调用QWidget::event(ev) 确保剩下的事件能继续上报并处理
        return QWidget::event(ev);
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iriczhao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值