Qt 鼠标点击事件穿透

【问题描述】
在近期开发的过程中遇到这样一个场景:透明的子窗口覆盖于父窗口之上,但需要父窗口中的部件对鼠标点击事件进行正常的响应。

正常情况下,由于子窗口覆盖于父窗口之上,鼠标点击事件会被子窗口中的部件获取,而不会被父窗口中的部件获取到,要解决这个问题,就需要让鼠标点击事件穿透子窗口,下发到父窗口的对应部件中。

【解决思路】
要实现事件的穿透,最关键的就是setAttribute(Qt::WA_TransparentForMouseEvents, true);的使用。
这个方法能将当前窗口及其子部件的鼠标事件屏蔽掉,由于我们希望鼠标点击时,是父窗口的对应部件对点击事件进行响应,所以在当前窗口的mousePressEvent()中,需要先调用此方法。之所以没有在构造函数中调用此方法,是因为如果在构造函数中调用,那么当前窗口的所有鼠标事件都不会被响应,而我的实际需求是当前窗口的部件对鼠标点击事件能够进行正常的响应,同时被当前窗口覆盖的父窗口中的部件对鼠标点击事件也能进行正常的响应,如图所示:
在这里插入图片描述
因此,setAttribute(Qt::WA_TransparentForMouseEvents, true);应在当前窗口的mousePressEvent()中调用,判断当前鼠标点击的位置是否属于某个父窗口部件,如果是,将鼠标点击事件下发给该部件,然后调用setAttribute(Qt::WA_TransparentForMouseEvents, false);取消对当前窗口及其子部件的鼠标事件屏蔽,以便后续当前窗口及其子部件能正常捕获鼠标事件。

【实现代码】

void ChildWidget::mousePressEvent(QMouseEvent *event)
{
    if (this->parentWidget())
    {
        //点下去的那一刻屏蔽当前窗口及其所有子部件的事件,此时被当前窗口遮挡住的父窗口的控件的事件被响应
        //为什么不到这里就结束,因为如果这样做,只要点击过一次,当前窗口及其子部件就再也不会响应鼠标事件,比如移入移出
        //因此点击之后还必须要把这个屏蔽取消掉
        //但如果中间什么也不做的话,这个属性的设置就没有意义,因为最终是false
        //所以需要手动将鼠标点击事件下发给父窗口中的对应部件
        this->setAttribute(Qt::WA_TransparentForMouseEvents, true);
        QPoint point = this->mapTo(this->parentWidget(), event->pos());    //将点击事件在当前部件的坐标转换为在父窗口坐标系中的坐标
        QWidget *widget = this->parentWidget()->childAt(point);   //判断当前点击的位置是否是父窗口的一个子部件
        if (widget)
        {
            point = widget->mapFrom(this->parentWidget(), point);    //将当前部件在父窗口的坐标转为在当前部件自己的坐标系中的坐标
            QMouseEvent *mouseEvent = new QMouseEvent(event->type(), point, event->button(), event->buttons(), event->modifiers());
            QApplication::postEvent(widget, mouseEvent);
        }
        this->setAttribute(Qt::WA_TransparentForMouseEvents, false);
    }
}

这样一来,点击当前窗口透明部分时,就能触发可见的父窗口子部件的鼠标点击事件了。

【参考博客】
QT 鼠标穿透


版权声明:本文为weixin_41001497原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。