Как отключить многократную автоматическую перерисовку при изменении размера виджетов в PyQt?



У меня есть программа PyQt4 с виджетами, содержимое которых перерисовывается очень медленно (это нормально, из-за моих задач). И когда я пытаюсь изменить размер этих виджетов, программа пытается перерисовать много раз, пока мышь не отпущена. Это очень много заморозков.



Я хочу отключить эту автоматическую перерисовку и настроить PyQt на перерисовку всех виджетов только при отпускании мыши (что означает, что перерисовка происходит ровно один раз за одно изменение размера).



Как это сделать?



Edit1. я буду смотрите на это довольно просто, вот так: вы перетаскиваете линию, и в то время как вы перетаскиваете, все виджеты стоят. Когда вы отпустите его, виджеты перерисовываются. Но я действительно не уверен, что это возможно в PyQt4.

672   1  

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_()

Вы заметите, что при быстром изменении размера главного окна никаких обновлений для пользовательского виджета не происходит, потому что мы отключили их в событии resize. 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, Чтобы также наблюдать за другими событиями, такими как перемещение.

Comments

    Ничего не найдено.