如何在PyQt中禁用调整小部件大小时的多重自动重绘?

5
我有一个PyQt4程序,其中的小部件内容重绘得非常缓慢(这没关系,因为我的任务)。当我尝试调整这些小部件的大小时,程序会在鼠标未释放时尝试多次重绘。这会导致很多冻结。
我想要禁用自动重绘,并配置PyQt只有在鼠标释放时才重绘所有小部件(这意味着每次调整大小只会发生一次重绘)。
如何实现?
编辑1. 我认为很简单,就像这样:你拖动线条时,所有小部件都停止。当你释放它时,小部件重新绘制。但我真的不确定在PyQt4中是否可能实现。

这是自定义绘制事件的结果还是默认小部件? - jdi
@jdi,主窗口上的默认小部件。例如,想象一下我正在尝试从QTreeWidget调整大小到QListWidget。 - Bruno Gelb
但是QTreeWidget只会绘制可见的项。您是否在其中加载了定制单元格小部件? - jdi
@jdi,我只是想说这是默认的小部件,不管具体是哪个。而且没有任何自定义绘制事件。 - Bruno Gelb
我问的原因是,如果你加载了很多小部件到一个视图中,那会导致性能问题。但我确实理解你正在寻找一种通用解决方案。下面我的回答涉及到如何处理调整大小事件。 - jdi
1个回答

9
首先,我建议确保如果您正在使用自定义绘图事件与您的小部件,则不要在每个事件中执行过重的工作,并寻找一种缓存或减少工作量的方法,而不是简单地寻找一个临时解决方案。否则...
绘制不透明或不绘制是由平台的窗口管理器决定的。据我所知,没有一个简单的属性可以切换此功能。类似于这样的东西存在于QSplitter上,只有在释放手柄后才进行绘制。
我可以提供一种变通的方法,即将更新延迟到一段时间内未发生大小调整之后。这将为您的应用程序提供一些空间来减少绘图事件。
from PyQt4 import QtCore, QtGui
import sys

class DelayedUpdater(QtGui.QWidget):

    def __init__(self):
        super(DelayedUpdater, self).__init__()
        self.layout = QtGui.QVBoxLayout(self)
        self.label = QtGui.QLabel("Some Text")
        self.layout.addWidget(self.label, QtCore.Qt.AlignCenter)

        self.delayEnabled = False
        self.delayTimeout = 100

        self._resizeTimer = QtCore.QTimer(self)
        self._resizeTimer.timeout.connect(self._delayedUpdate)

    def resizeEvent(self, event):
        if self.delayEnabled:
            self._resizeTimer.start(self.delayTimeout)
            self.setUpdatesEnabled(False)

        super(DelayedUpdater, self).resizeEvent(event)

    def _delayedUpdate(self):
        print "Performing actual update"
        self._resizeTimer.stop()
        self.setUpdatesEnabled(True)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    win = QtGui.QMainWindow()
    view = DelayedUpdater()
    win.setCentralWidget(view)
    win.show()
    view.delayEnabled = True
    app.exec_()

您会注意到,当您快速调整主窗口大小时,自定义小部件没有更新,因为我们在调整大小事件中将其关闭了。一个QTimer正在尝试每100毫秒执行更新并停止自身。但每次发生另一个调整大小事件时,它都会重新启动计时器。效果是计时器将继续被重置,禁用更新,直到出现延迟。
尝试按住鼠标,稍微调整大小,等待一下,然后再调整大小。即使您的鼠标按下但未调整大小,更新也应该发生。调整延迟以适合您的需要。您可以使用布尔标志控制打开或关闭该功能。
此示例还可以重新设计,使DelayedUpdater成为QObject,接受某个QWidget实例作为参数。然后它将自己设置为该对象的eventFilter并监视其resizeEvent。这样,您不必为添加此内容而对常规小部件进行子类化。您只需创建一个DelayedUpdater实例并将其用作实用程序对象来监视小部件即可。
以下是将其制作为辅助对象的示例:
class MainWindow(QtGui.QMainWindow):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.someWidget = QtGui.QWidget()
        self.setCentralWidget(self.someWidget)

        self.layout = QtGui.QVBoxLayout(self.someWidget)
        self.label = QtGui.QLabel("Some Text")
        self.layout.addWidget(self.label, QtCore.Qt.AlignCenter)

        self.delayer = DelayedUpdater(self.someWidget)


class DelayedUpdater(QtCore.QObject):

    def __init__(self, target, parent=None):
        super(DelayedUpdater, self).__init__(parent)
        self.target = target
        target.installEventFilter(self)

        self.delayEnabled = True
        self.delayTimeout = 100

        self._resizeTimer = QtCore.QTimer()
        self._resizeTimer.timeout.connect(self._delayedUpdate)

    def eventFilter(self, obj, event):
        if self.delayEnabled and obj is self.target:
            if event.type() == event.Resize:
                self._resizeTimer.start(self.delayTimeout)
                self.target.setUpdatesEnabled(False)

        return False

    def _delayedUpdate(self):
        print "Performing actual update"
        self._resizeTimer.stop()
        self.target.setUpdatesEnabled(True)

请注意,我们仅在主窗口中的某个任意小部件上使用它。我们使用以下代码将延迟更新器添加到该小部件中:
self.delayer = DelayedUpdater(self.someWidget)
< p > DelayedUpdater 监听目标小部件的大小调整事件,并执行延迟更新。您可以扩展 eventFilter 以同时监视其他事件,例如移动。


谢谢你的提交,这是一个有用的解决方法!但还存在一些问题。首先,由于某些原因,您的示例在几次“实际更新”(主窗口移动)后会挂起。然后它看起来像这样:http://i.imgur.com/pnY2m.jpg,并且只显示垃圾。其次,您的解决方法仅适用于主窗口移动。这很好,但窗口移动只是重新绘制的情况之一。还有QSplitter移动和QDockWidget移动,这似乎现在无法使用。但不幸的是,我最需要的正是这两个 :(此致敬礼。 - Bruno Gelb
这不仅适用于主窗口,也适用于任何QWidget。在这种情况下,您只需观察调整大小事件即可。没有全局不透明的调整大小选项。 - jdi
@mivulf:抱歉,我在那里留下了一行不该有的代码。我删除了processEvents调用。那只是为了测试而已,对我来说也会崩溃。这个点不应该被锁定。如果您需要查看使用事件过滤器的实用程序QObject的示例,请告诉我。 - jdi
哇!^_^非常感谢!它完美地运行了!现在我很开心。但是我不能给你点赞,因为我还没有足够的声望。 - Bruno Gelb
谢谢!这正是我今天所需要的 :-) - Michael Clerx

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