如何强制Qt更新鼠标下的窗口部件?

4
在Windows 7上,具体而言,虽然我认为这并不重要。
我们在无数个桌面应用程序中都看到过这个问题,特别是那些不使用操作系统提供的控件的游戏:当屏幕在静止的鼠标光标下编程更改时(与用户移动光标到新的小部件相反),它们会失去同步。光标要么不改变,要么小部件不按照应该将光标放进去的方式绘制 - 显然,小部件的“鼠标进入”事件没有被触发。如果你轻轻摇动鼠标而甚至没有离开小部件,事情就会自己解决。
不幸的是,Qt 5.7也有这个普遍存在的问题。首先想到的解决方案是通过Windows手段将鼠标程序化地移动到(0,0),然后再返回。然而,这不是跨平台的。还有更好的想法吗?

“the screen changes programmatically” 的确切含义是什么?您在Qt中遇到了什么问题? - Kamajii
@Kamajii 我有一个继承自QSplitter的分割器,其中包含一系列继承自QPanel的面板。每个面板都有一个关闭按钮。当用户点击此按钮时,面板将关闭,并且分隔器会调整其他面板的大小,以便新面板占用其空间。但是,对于现在位于鼠标光标下的新面板,enterEvent()没有被调用,因此面板被错误地绘制。摇动鼠标可以解决这个问题。 - sigil
在鼠标所在的小部件上调用QWidget::update是否解决了你的问题? - m7913d
@m7913d是我尝试的第一件事。但它不起作用。 - sigil
这是一个Qt的bug。您可以通过合成所需的事件来解决它。 - Kuba hasn't forgotten Monica
显示剩余3条评论
2个回答

1
我不知道你的问题是否仍然有效。
你可以使用以下代码覆盖方法underMouse()
bool MyWidget::underMouse()
{
    return rect().contains(mapFromGlobal(QCursor::pos()));
}

当分隔器调整面板大小时,其余面板中的 moveEventresizeEvent 事件将被触发。您只需要检查鼠标下是否有小部件,然后手动调用 enterEvent() 即可。


准确地说,您无法覆盖此方法,因为它不是虚拟的。 - HiFile.app - best file manager
最好使用QApplication::widgetAt()而不是widget->rect().contains(),因为在后一种情况下,我们会得到与其他覆盖的小部件相比错误的结果。 - HiFile.app - best file manager

0

我找到了两个解决方案。

第一个是一个简单的hack:调用widget->hide(),然后立即调用widget->show()。这将重新评估和更新小部件的视觉状态,取决于它是否在鼠标光标下,即使光标没有移动。但我不建议使用这个解决方案,因为它可能会产生一些不必要的副作用。虽然我还没有遇到过任何问题。

第二个解决方案更好,因为它看起来不像是一个hack,而且它可能没有任何副作用:

widget->setAttribute(Qt::WA_UnderCursor, qApp->widgetAt(QCursor::pos()) == widget);
widget->update();

假设代码是从小部件的父级调用的。但您可以进行调整并从任何其他位置进行调用。注意:最好使用QApplication :: widgetAt()而不是widget-> rect()。包含(),这是在其他答案中建议的,因为在后一种情况下,我们会对被其他小部件覆盖的小部件得到错误的结果。

实际上比较复杂的是找到应该从哪里调用此代码的代码位置。因为小部件的运动可能有许多来源-在其父级内移动,父级的移动,调整大小,父级的调整大小,滚动等。这可能是为什么将其实现到标准Qt小部件库中会太复杂的原因。在某些情况下,这可能会导致性能问题。(我猜测)

只是为了展示我的用法:我正在移动一个包含许多子小部件的整个容器小部件。随后,在容器移动后,其中一个子小部件可能会出现在光标下。所以我调用:

containerWidget->move(dx, dy); // this moves the container
for (QWidget *child : containerWidget->findChildren<QWidget*>())
{
    child->setAttribute(Qt::WA_UnderMouse, qApp->widgetAt(QCursor::pos()) == child);
    child->update();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接