【问题标题】:Why do widgets flicker out of position, when showing them inside a QGridlayout for the first time?为什么第一次在 QGridlayout 中显示小部件时,小部件会闪烁错位?
【发布时间】:2020-01-03 11:51:52
【问题描述】:

我有一个带有很多小部件的QGridLayout,我需要经常使用它们。我这样做是按照新排序的顺序一一替换它们。

然而,这意味着,对于每个添加的小部件,布局都希望更新自身并重新绘制其子级,这会导致很多不必要的减速。

我想告诉布局不要做任何布局或绘图,直到我完成添加我想要添加的所有内容。

我正在尝试通过覆盖 update() 方法并仅在添加完所有小部件后调用父方法来做到这一点。

    def update(self):
        self._fill_layout()

        super(MyLayout, self).update()

这似乎仍然没有消除所有的减速,所以我还捕获了对 setGeometry() 的所有调用,并且仅在最后一次调用它的 20 毫秒后调用它的父版本。

    def setGeometry(self, screen_rect=None):
        if screen_rect is not None:
            self._last_requested_geometry = screen_rect

            self._delayed_redraw_timer.start(20)
        else:
            # called by our timer
            return super(MyLayout, self).setGeometry(self._last_requested_geometry)

然而,这会导致一些小部件被显示,虽然它们不是布局的一部分,而只是父小部件的一部分(我假设,因为它们位于左上角并且比它们应该的更大。所以它们似乎是画出来的,而我还在布置它们。

我为小部件安装了一个事件过滤器,具体取决于它们是否在布局中。但这似乎产生了相反的效果,因为小部件现在没有绘制(更准确地说,只有背景是)。我假设给我带来麻烦的绘制事件被阻止了,但之后没有其他事件发生,所以它们被放入布局但不再重绘?

    def eventFilter(self, watched, event) -> bool:
        if event.type() == QEvent.Paint:
            return watched.layout() is None

        return False

我尝试通过手动触发重绘来解决此问题,在 self.parentWidget().repaint() 布局中使用 update 方法,但这不会改变任何东西。小部件仍未显示。

有没有更简单的方法来告诉布局只计算自己,而不更新其视觉效果,然后手动触发该绘制事件?

【问题讨论】:

  • 您可能想要使用QWidgetupdatesEnabled 属性。
  • @G.M.哪个小部件?布局所在的那个?
  • 是的。根据应该相应地启用/禁用子小部件的文档。
  • @G.M.不幸的是,不会改变我目睹的任何行为。

标签: qt pyside2 qlayout


【解决方案1】:

我找到了解决我的特定问题的方法,正如预期的那样,它很复杂。

在我的小部件中,我有一个 Gridlayout,我用它基本上绘制一个条目表,可以将其设置为组中可见或不可见(扩展子表)。

当添加新数据时,只有一行可见,其余行及其小部件被添加为“隐藏”,这使得布局在更新时忽略它们。

这意味着,每当我第一次想要扩展一行时,它的所有子行及其小部件都将变为可见,并且布局将重新更新每个小部件的状态。在我的情况下,这可能会连续发生几十次。

为了避免这种情况,我在布局类的 setGeometry() 调用中添加了一个 QTimer,以连续捕获几十个调用并等待几毫秒并在每次调用时重置计时器。只有当没有新的呼叫进来时,我才会让setGeometry() 呼叫通过并允许布局自行更新。

这使布局方式更具响应性,但现在闪烁的小部件开始了。当我展开一行时,我突然看到作为新行一部分的小部件在主小部件的左上角出现过大的瞬间。

起初我认为这意味着它们会在QTimer 仍处于活动状态时显示。它们显示为父级的子级,尚未成为布局的一部分。事实并非如此,但是,它们始终是布局的一部分,但是自从我添加它们后,当它们不可见时,它们尚未调整大小或正确定位,并且使用 @987654324 @我在推迟那个动作,而不是推迟他们的绘画活动。这意味着它们在被布局定位之前被绘制,然后再次绘制,当这种情况发生时,当计时器用完时。

一旦我注意到这一点,我就可以安装一个事件侦听器来侦听绘制事件,并为位置 (0, 0) 的所有小部件禁用它们。解决问题的粗略方法,但我愿意去那里。

然而,这并没有完全解决问题。我仍然可以看到一个大的空方块出现,小部件之前就在那里。这很明显,因为连续的小部件具有与正常灰色不同的背景/窗口颜色。

我认为这意味着该行的一个空“单元格”仍然以某种方式绘制,并且我没有在我的事件侦听器中捕获它。然而,经过更多研究,我发现窗口或小部件的背景不是在绘制事件中绘制的,而是在之前,似乎与我可以捕捉到的任何特定事件分开。

这意味着我一开始看到的小部件仍然存在,只是没有被绘制,因此只有它的背景被显示。

由于我找不到阻止小部件背景绘制的方法,我选择只从小部件中移除背景颜色,因为它只是稍微浅一点的灰色阴影。

我知道这显然是一个迹象,表明我没有使用正确的方法,但沉没成本谬误 + 没有时间让我暂时坚持下去。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-15
    • 1970-01-01
    • 2015-05-29
    • 2011-07-20
    • 2020-10-16
    • 2013-05-08
    相关资源
    最近更新 更多