如何将鼠标事件传递给被遮挡的控件?

ison_jaro 2014-07-15 05:30:47
如图,绿色的控件对下方红色的控件有部分遮挡,鼠标点击这部分遮挡区域的时候红色控件无法接收到事件。
试过设置绿色控件的Qt::WA_TransparentForMouseEvents属性,但这样一来它上面的按钮也废掉了。
有没有办法既不影响按钮的功能,又能让后面的红色控件接收到鼠标事件呢?
...全文
2716 20 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
e_wuweiping 2014-12-24
  • 打赏
  • 举报
回复
楼主, 同样的问题,如果绿色控制是用QML写的,那么要怎么处理鼠标事件呢?
dbzhang800 2014-07-21
  • 打赏
  • 举报
回复
感觉上,你的需求比较怪异,不太明白,为什么需要这样一个绿色控件始终显示呢?不能需要它时才显示,其他时候都隐藏么?
ison_jaro 2014-07-21
  • 打赏
  • 举报
回复
还是没有可用的回复。。。
重新发个图吧,楼主需要的是:
1. 绿色区域内用界面布局设置了上下左右4个按钮,这4个按钮必须能够响应点击操作(click事件);
2. 红色控件为可拖拽控件(响应按住鼠标左键并移动鼠标),它在绿色区域后面时拖拽不会失效(现在会失效);
ison_jaro 2014-07-21
  • 打赏
  • 举报
回复
问题已解决,结贴了。 实际上回帖中没有可用的解决方案,但11楼的朋友@foxyz给出的提示最接近最终的方案(QApplication::postEvent),给他结了20分。其他认真回复的朋友每贴给了5分。 说一下解决问题的过程: 1. 查找QT源代码中用到Qt::WA_TransparentForMouseEvents的地方,发现在QApplication的widgetAt()和QWidget的childAt()方法中用到了。说明这个属性只会影响控件的查找,而不是事件的传递: in QApplication.cpp:

QWidget *QApplication::widgetAt(const QPoint &p)
{
    QWidget *window = QApplication::topLevelAt(p);
    if (!window)
        return 0;

    QWidget *child = 0;

    if (!window->testAttribute(Qt::WA_TransparentForMouseEvents))
        child = window->childAt(window->mapFromGlobal(p));

    if (child)
        return child;

   // ....
}
in QWidget.cpp:

QWidget *QWidget::childAt(const QPoint &p) const
{
    return d_func()->childAt_helper(p, false);
}

QWidget *QWidgetPrivate::childAt_helper(const QPoint &p, bool ignoreChildrenInDestructor) const
{
    if (children.isEmpty())
        return 0;
    // ...
    return childAtRecursiveHelper(p, ignoreChildrenInDestructor);
}

QWidget *QWidgetPrivate::childAtRecursiveHelper(const QPoint &p, bool ignoreChildrenInDestructor, bool includeFrame) const
{
#ifndef Q_WS_MAC
    Q_UNUSED(includeFrame);
#endif
    for (int i = children.size() - 1; i >= 0; --i) {
        QWidget *child = qobject_cast<QWidget *>(children.at(i));
        if (!child || child->isWindow() || child->isHidden() || child->testAttribute(Qt::WA_TransparentForMouseEvents)
            || (ignoreChildrenInDestructor && child->data->in_destructor)) {
            continue;
        }
        // ...
    }
    return 0;
}
2. QWidget::mousePressEvent()调的是event->ignore();QAbstractButton::mousePressEvent调用的是event->accept();。 3. 打印接收到鼠标事件的控件名,发现调用event->ignore()之后,消息被传递给了父控件,而调用event->accept();之后消息就不再传递了。 根据这3个条件,可以让绿色控件截获到鼠标事件后,“绕过”自己,向后面的控件发送消息:

void 绿色控件::mouseMoveEvent( QMouseEvent *e )
{
	QWidget::mouseMoveEvent(e);
	MyMouseEvent(e);
}

void 绿色控件::mousePressEvent( QMouseEvent *e )
{
	QWidget::mousePressEvent(e);
	MyMouseEvent(e);
}

void 绿色控件::mouseReleaseEvent( QMouseEvent *e )
{
	QWidget::mouseReleaseEvent(e);
	MyMouseEvent(e);
}

void 绿色控件::mouseDoubleClickEvent( QMouseEvent *e )
{
	QWidget::mouseDoubleClickEvent(e);
	MyMouseEvent(e);
}

void 绿色控件::MyMouseEvent( QMouseEvent *e )
{
	if (this->parentWidget())
	{
		// 将自己设为鼠标事件透明并重新搜索是否有后面的控件会响应鼠标事件。
		this->setAttribute(Qt::WA_TransparentForMouseEvents, true);
		QPoint pt = this->mapTo(this->parentWidget(), e->pos());
		QWidget *w = this->parentWidget()->childAt(pt);
		if (w)
		{
			pt = w->mapFrom(this->parentWidget(), pt);
			QMouseEvent *event = new QMouseEvent(e->type(), pt, e->button(), e->buttons(), e->modifiers());
			QApplication::postEvent(w, event);
		}
		// 将自己设为鼠标事件不透明,以免影响button的功能
		this->setAttribute(Qt::WA_TransparentForMouseEvents, false);
	}
}
foruok 2014-07-18
  • 打赏
  • 举报
回复
引用 13 楼 ison_jaro 的回复:
[quote=引用 10 楼 foruok 的回复:] 有个建议,可以使用 QGraphicsView 来实现。绿色的 QGraphicsItem 不接受鼠标事件即可,它作为 button 的背景。红色的z序设置为 0 , 绿色为 1, button 为 2。这样就又有遮罩效果,又可以鼠标事件透传。需要你派生 QGraphicsItem 实现一个 button ,不过这也是分分钟的事儿。
能详细说说吗?比如上面的图,黑色的控件继承QGraphicsView,红色和绿色控件以及button都继承QGraphicsItem? 这样的话会不会不能在界面编辑器中布局? 另外,QGraphicsView并不是继承自QWidget,如果自定义控件需要继承他们两个,是否涉及到了多重继承的问题? [/quote] 不能在界面编辑器内布局,只需要继承 QGraphicsItem ,相当于:如果让一切重来……不过很灵活。 给我投票了吗,兄弟?博文决赛,投我一票,谢谢。
ison_jaro 2014-07-18
  • 打赏
  • 举报
回复
引用 11 楼 foxyz 的回复:
简单来说:假设你有两个控件 widgetA, widgetB的对象 objA,objB 你想把A的某些event转发给B,在A里自己实现virtual void event(QEvent* e) 截获需要发给B的事件,然后复制该event(简单来说就是 new newevent, copy e to newevent), 然后qApp->postEvent(newevent, objB); 或者QApplication::postEvent(newEvent, objB)
谢谢回复,但我需要的不是转发,而是不遮挡。 因为接收这个被遮挡事件的并不是固定不动的控件。
ison_jaro 2014-07-18
  • 打赏
  • 举报
回复
引用 10 楼 foruok 的回复:
有个建议,可以使用 QGraphicsView 来实现。绿色的 QGraphicsItem 不接受鼠标事件即可,它作为 button 的背景。红色的z序设置为 0 , 绿色为 1, button 为 2。这样就又有遮罩效果,又可以鼠标事件透传。需要你派生 QGraphicsItem 实现一个 button ,不过这也是分分钟的事儿。
能详细说说吗?比如上面的图,黑色的控件继承QGraphicsView,红色和绿色控件以及button都继承QGraphicsItem? 这样的话会不会不能在界面编辑器中布局? 另外,QGraphicsView并不是继承自QWidget,如果自定义控件需要继承他们两个,是否涉及到了多重继承的问题?
ison_jaro 2014-07-18
  • 打赏
  • 举报
回复
引用 15 楼 foruok 的回复:
不能在界面编辑器内布局,只需要继承 QGraphicsItem ,相当于:如果让一切重来……不过很灵活。 给我投票了吗,兄弟?博文决赛,投我一票,谢谢。
这样的话我还是用不上啊。。。相当于整个推翻我现有的框架了。。而且用代码写界面什么的太不适应了。
donwmufromdying 2014-07-17
  • 打赏
  • 举报
回复
简单来说:假设你有两个控件 widgetA, widgetB的对象 objA,objB 你想把A的某些event转发给B,在A里自己实现virtual void event(QEvent* e) 截获需要发给B的事件,然后复制该event(简单来说就是 new newevent, copy e to newevent), 然后qApp->postEvent(newevent, objB); 或者QApplication::postEvent(newEvent, objB)
foruok 2014-07-17
  • 打赏
  • 举报
回复
有个建议,可以使用 QGraphicsView 来实现。绿色的 QGraphicsItem 不接受鼠标事件即可,它作为 button 的背景。红色的z序设置为 0 , 绿色为 1, button 为 2。这样就又有遮罩效果,又可以鼠标事件透传。需要你派生 QGraphicsItem 实现一个 button ,不过这也是分分钟的事儿。
donwmufromdying 2014-07-17
  • 打赏
  • 举报
回复
QApplication::postEvent()
ison_jaro 2014-07-17
  • 打赏
  • 举报
回复
没人回复了吗?再顶一下。
  • 打赏
  • 举报
回复
installEventFilter
ison_jaro 2014-07-16
  • 打赏
  • 举报
回复
引用 5 楼 sddsighhz 的回复:
这个工作量其实不大,你在eventFilter里面只要拦截鼠标按下和鼠标弹起的事件啊,拦截到再发给对应的控件就可以了
而且红色的控件是移动的,如果按你说的发给对应的控件,那么我还必须检测当前事件的位置是否在红色控件的区域内,这又涉及到了一大堆的坐标转换…… 综上所述,工作量挺大的。 所以,其实我想要的是:绿色的控件不会遮挡任何鼠标事件。 这一点Qt::WA_TransparentForMouseEvents属性可以满足要求,但又会使它上面的button失效。
ison_jaro 2014-07-16
  • 打赏
  • 举报
回复
引用 5 楼 sddsighhz 的回复:
这个工作量其实不大,你在eventFilter里面只要拦截鼠标按下和鼠标弹起的事件啊,拦截到再发给对应的控件就可以了
如何发送呢?调用红色控件的event()方法?可是event()方法是protected啊。 那么就需要拦截MouseButtonPress、MouseButtonRelease、MouseButtonDblClick、MouseMove四种事件,然后在红色控件里提供处理4种事件的public的方法? 想想都头大。。 因为在我实际的程序里,红色的控件可能是N多种不同的自定义控件。 而且这样做对程序的通用性也不好。如果红色控件是个外部dll提供的控件我就无法修改它的代码了。
Creator_莫言 2014-07-16
  • 打赏
  • 举报
回复
引用 4 楼 ison_jaro 的回复:
[quote=引用 2 楼 sddsighhz 的回复:] 你自己实现eventFilter,然后把控件都加到eventFilter里面就可以自己控制啦
这样做工作量太大了吧? 能不能简单的把事件传递给下面的控件呢?[/quote] 这个工作量其实不大,你在eventFilter里面只要拦截鼠标按下和鼠标弹起的事件啊,拦截到再发给对应的控件就可以了
ison_jaro 2014-07-16
  • 打赏
  • 举报
回复
引用 2 楼 sddsighhz 的回复:
你自己实现eventFilter,然后把控件都加到eventFilter里面就可以自己控制啦
这样做工作量太大了吧? 能不能简单的把事件传递给下面的控件呢?
ison_jaro 2014-07-16
  • 打赏
  • 举报
回复
引用 1 楼 foruok 的回复:
貌似混淆啊,挡住了为么还要接收事件?不合逻辑啊
实际的使用上绿色控件是个导航面板,上面有上下左右N个按钮,按钮周围的区域是透明的。
Creator_莫言 2014-07-15
  • 打赏
  • 举报
回复
你自己实现eventFilter,然后把控件都加到eventFilter里面就可以自己控制啦
foruok 2014-07-15
  • 打赏
  • 举报
回复
貌似混淆啊,挡住了为么还要接收事件?不合逻辑啊

16,809

社区成员

发帖
与我相关
我的任务
社区描述
Qt 是一个跨平台应用程序框架。通过使用 Qt,您可以一次性开发应用程序和用户界面,然后将其部署到多个桌面和嵌入式操作系统,而无需重复编写源代码。
社区管理员
  • Qt
  • 亭台六七座
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧