Qt小部件何时收到paintEvent事件?

21

我想知道在哪些情况下widget会收到它的paint事件,以及这如何随着操作系统而变化。

Qt文档中对于paintEvent只是简单地说明:

绘制事件是请求重绘部件全部或部分的一个操作。此操作可能发生在以下一种或多种情况下:

调用repaint()或update()函数,

部件被遮挡后又重新显示出来,或者

其他很多原因。

目前为止,我已经在paintEvent中加入了一些跟踪代码,

void Widget::paintEvent(QPaintEvent *e)
{
    static int count = 0;
    qDebug("paintEvent, %d", count++);
}

这是我在Windows 7上发现的情况:

当小部件失去/获得焦点时,将调用paintEvent。当另一个小部件经过我们的小部件时,不会调用 paintEvent。我不知道这是否归因于Windows 7合成。还有,当最小化的窗口被恢复时,也会调用paintEvent。在调整大小时也会调用paintEvent。

那么这种行为是否依赖于操作系统呢?

1个回答

22

是的,从您所描述的意义上来说,它取决于操作系统。

桌面窗口管理器(DWM)是 Windows Vista 和 7 中的一个组件,负责桌面合成、Aero Glass 效果和各种其他视觉效果。它与之前版本的 Windows 使用的模型有些不同。正如您所怀疑的那样,它会缓存您窗口的位图,即使这些窗口因为被其他窗口遮挡而不可见。也就是说,它不需要重绘它们(因此不会引发绘画事件),因为它可以直接从缓存的位图中复制它们。这不仅是相对于让每个应用程序自己重绘来说的一个潜在的优化,而且还允许 DWM 实现像 Aero Flip 这样的功能,其中它使用其缓存的位图。

唯一的例外是一直存在的,比如 CS_SAVEBITS 类风格。如果 DWM 缓存的位图已失效(例如因为您的窗口图片已更改),它会放弃该位图并要求您重新绘制窗口。

通过关闭 DWM 合成(切换到“Windows 经典”主题),然后遮挡您的窗口来测试这个理论,看看是否接收到绘画事件。就像在所有之前的 Windows 版本中一样,您应该会接收到。

但更重要的是,您不应该依赖于按特定顺序接收绘画事件。关于绘画事件,您唯一可以假设的事情是,当操作系统需要您重新绘制您的窗口时,您将会收到一个绘画事件。否则,它不会打扰您。我相信这就是文档在这一点上充满含糊的原因,超出了可能的技术限制。

这就是为什么逻辑不应该放在绘画事件处理程序中的原因。该方法唯一要负责的事情是通过其当前状态重绘窗口。该状态需要在其他地方保存。这条规则也是可交换的:您不应该在绘画事件处理程序之外进行任何绘画

当然,你总是可以通过使窗口无效 (我相信Qt有一个invalidaterefresh方法来实现这一点,请查阅文档) 来强制触发绘画事件,但这并不意味着在处理此事件的方法中放置应用程序逻辑是一个好的模式。


1
请注意,每当另一个位于我们正在讨论的QWidget之上/之下/移动/调整大小时,都会发生重绘。至少这是默认行为。在这种情况下,paintEvent将使用特定区域(QRegion)作为QPaintEvent的参数进行调用,该区域描述要更新的区域。利用这些信息,可以优化重新绘制内容所需的步骤,但我认为这很少使用。 - leemes

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