PyQt - 没有重绘所有内容的动画?

时间:2016-08-03 20:34:21

标签: python qt user-interface animation pyqt

所以我试图在PyQt中学习动画,在我可以在网上找到的所有例子中,他们似乎都使用self.update()self.repaint()方法来增加动画。这意味着基本上代码必须擦除然后重绘每个帧的整个小部件,即使我打算制作动画的大部分都是静态的。

例如下面的代码,这将生成一个循环进度饼。重要的一点是paint()(第一类)下的ProgressMeter方法:对于动画中的每个帧,此示例绘制背景,实际进度饼和百分比指示符。

如果我将代码更改为:

if self.angle > 120:
    # do not draw background

然后在120帧之后,背景不再被绘制。

这看起来非常低效,因为(逻辑上)背景应该只绘制一次,不是吗?

你会为这类动画推荐什么?

附录:我在这个网站上潜伏了很多东西来窃取示例和代码,但很长一段时间没有发布。如果我没有正确地遵守礼仪,请告诉我正确的礼节等。

import sys
from PyQt4 import QtGui, QtCore


class ProgressMeter(QtGui.QGraphicsItem):
    def __init__(self, parent):
        super(ProgressMeter, self).__init__()

        self.parent = parent

        self.angle = 0
        self.per = 0

    def boundingRect(self):

        return QtCore.QRectF(0, 0, self.parent.width(),
                             self.parent.height())

    def increment(self):

        self.angle += 1
        self.per = int(self.angle / 3.6)

        if self.angle > 360:
            return False
        else:
            return True

    def paint(self, painter, option, widget):

        self.drawBackground(painter, widget)
        self.drawMeter(painter, widget)
        self.drawText(painter)

    def drawBackground(self, painter, widget):

        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.setPen(QtCore.Qt.NoPen)

        p1 = QtCore.QPointF(80, 80)
        g = QtGui.QRadialGradient(p1 * 0.2, 80 * 1.1)

        g.setColorAt(0.0, widget.palette().light().color())
        g.setColorAt(1.0, widget.palette().dark().color())
        painter.setBrush(g)
        painter.drawEllipse(0, 0, 80, 80)

        p2 = QtCore.QPointF(40, 40)
        g = QtGui.QRadialGradient(p2, 70 * 1.3)

        g.setColorAt(0.0, widget.palette().midlight().color())
        g.setColorAt(1.0, widget.palette().dark().color())
        painter.setBrush(g)
        painter.drawEllipse(7.5, 7.5, 65, 65)

    def drawMeter(self, painter, widget):

        painter.setPen(QtCore.Qt.NoPen)
        painter.setBrush(widget.palette().highlight().color())
        painter.drawPie(7.5, 7.5, 65, 65, 0, -self.angle * 16)

    def drawText(self, painter):

        text = "%d%%" % self.per

        font = painter.font()
        font.setPixelSize(11)
        painter.setFont(font)
        brush = QtGui.QBrush(QtGui.QColor("#000000"))
        pen = QtGui.QPen(brush, 1)
        painter.setPen(pen)
        # size = painter.fontMetrics().size(QtCore.Qt.TextSingleLine, text)
        painter.drawText(0, 0, 80, 80,
                         QtCore.Qt.AlignCenter, text)


class MyView(QtGui.QGraphicsView):
    def __init__(self):
        super(MyView, self).__init__()

        self.initView()
        self.setupScene()
        self.setupAnimation()

        self.setGeometry(300, 150, 250, 250)

    def initView(self):
        self.setWindowTitle("Progress meter")
        self.setRenderHint(QtGui.QPainter.Antialiasing)

        policy = QtCore.Qt.ScrollBarAlwaysOff
        self.setVerticalScrollBarPolicy(policy)
        self.setHorizontalScrollBarPolicy(policy)

        self.setBackgroundBrush(self.palette().window())

        self.pm = ProgressMeter(self)
        self.pm.setPos(55, 55)

    def setupScene(self):
        self.scene = QtGui.QGraphicsScene(self)
        self.scene.setSceneRect(0, 0, 250, 250)
        self.scene.addItem(self.pm)

        self.setScene(self.scene)

    def setupAnimation(self):
        self.timer = QtCore.QTimeLine()
        self.timer.setLoopCount(0)
        self.timer.setFrameRange(0, 100)

        self.animation = QtGui.QGraphicsItemAnimation()
        self.animation.setItem(self.pm)
        self.animation.setTimeLine(self.timer)

        self.timer.frameChanged[int].connect(self.doStep)
        self.timer.start()

    def doStep(self, i):
        if not self.pm.increment():
            self.timer.stop()

        self.pm.update()


app = QtGui.QApplication([])
view = MyView()
view.show()
sys.exit(app.exec_())

1 个答案:

答案 0 :(得分:0)

Qt关于QWidget重绘插槽的文档说:

通过立即调用paintEvent()直接重新绘制窗口小部件,除非禁用更新或隐藏窗口小部件。 如果您需要立即重新绘制,我们建议仅使用repaint(),例如在动画期间。在几乎所有情况下,update()都更好,因为它允许Qt优化速度并最大限度地减少闪烁。 警告:如果在可能从paintEvent()调用的函数中调用repaint(),则可能会获得无限递归。 update()函数永远不会导致递归。

这应该会给你一个关于何时使用或不重新绘制或更新插槽的答案。

关于制作动画我建议您也要查看Qt4's animation frameworkQt5's animation framework,这是在Qt上制作小部件动画的一种非常强大的方法。