【问题标题】:How to disable multiple auto-redrawing at resizing widgets in PyQt?如何在 PyQt 中调整小部件的大小时禁用多个自动重绘?
【发布时间】:2012-11-25 15:18:28
【问题描述】:

我有一个带有小部件的 PyQt4 程序,其内容重绘非常缓慢(没关系,因为我的任务)。当我尝试调整这些小部件的大小时,程序会在未释放鼠标的情况下尝试重绘很多次。这是很多冻结。

我想禁用自动重绘并将 PyQt 配置为仅在释放鼠标时重绘所有小部件(这意味着每次调整大小时重绘只会发生一次)。

怎么做?

Edit1. 我会很简单地看到它,就像这样:拖动线条,并且在拖动时,所有小部件都站立。当您释放它时,小部件会重绘。但我真的不确定在 PyQt4 中是否可行。

【问题讨论】:

  • 这是自定义绘制事件的结果还是这些默认小部件?
  • @jdi,主窗口上的默认小部件。例如,假设我将尝试从 QTreeWidget 调整为 QListWidget。
  • 但是 QTreeWidget 只会绘制可见的项目。您是否使用自定义单元小部件大量加载它?
  • @jdi,我只是想说明它是默认小部件,无论具体是哪个。并且没有任何自定义绘制事件。
  • 我问的原因是因为如果你正在加载一个带有一堆小部件的视图,那将导致性能问题。但我确实了解您正在寻找通用解决方案。我在下面的回答谈到了处理调整大小事件。

标签: performance qt pyqt pyqt4 qpainter


【解决方案1】:

首先,我建议确保如果您在小部件中使用自定义绘制事件,那么您不会在每个事件中做太多的工作,而只是寻找一种创可贴的解决方案。如果是这种情况,请尝试找到一种方法来缓存或减少工作。否则……

是否绘制不透明的决定是由您平台的窗口管理器做出的。据我所知,没有一个简单的属性可以切换此功能。 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 毫秒触发一次以执行更新并自行停止。但是每次发生另一个调整大小事件时,它都会重新启动该计时器。效果是计时器将继续被重置。禁用更新,直到出现延迟。

尝试按住鼠标,稍微调整大小,等待,然后再调整大小。即使您的鼠标按下但您没有调整大小,更新也应该发生。调整延迟以适应。您可以使用 bool 标志控制打开和关闭该功能。

这个例子也可以重新设计,使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)

DelayedUpdater 监视目标小部件的调整大小事件,并执行延迟更新。您可以扩展 eventFilter 以同时监视其他事件,例如移动。

【讨论】:

  • 嗯,感谢您的提交,这是有用的解决方法!但仍然存在一些问题。首先,由于某些原因,您的示例在几次“实际更新”(主窗口移动)后暂停。然后它看起来像这样:i.imgur.com/pnY2m.jpg 并且只显示废话。其次,您的解决方法仅有助于主窗口移动。这很好,但窗口移动只是重绘案例之一。还有 QSplitter 移动和 QDockWidget 移动,现在似乎可以使用它。但不幸的是,正是我最需要的那两个:(真诚的你。
  • 这不是特定于主窗口的。它适用于任何 QWidget。在这种情况下,您只是在观看调整大小事件。没有全局不透明调整大小选项。
  • @mivulf:对不起。我在那里留下了一行不应该存在的代码。我删除了processEvents 电话。那只是为了测试并且对我来说也崩溃了。此时不应锁定。如果您需要查看使用事件过滤器的实用程序 QObject 的示例,请告诉我
  • 哇! ^_^ 非常感谢!它完美地工作!现在我很高兴。但我不能给你投票,因为我还没有足够的声望。
  • 谢谢!这正是我今天所需要的:-)
猜你喜欢
  • 2017-11-14
  • 1970-01-01
  • 2010-11-12
  • 1970-01-01
  • 1970-01-01
  • 2017-11-04
  • 2015-04-08
  • 2016-09-08
  • 2011-04-14
相关资源
最近更新 更多