title: PyQt - Animations without redrawing everything?
body: <p>So I'm trying to learn animations in PyQt and throughout all the examples I can find online, they all seem to use the <code>self.update()</code> or <code>self.repaint()</code> methods to increment the animations. This means basically the code has to erase then redraw the entire widget for every frame, even though much of what I intend to animate is static.</p>
  
  <p>For example the code below, this generates a circular progress pie. The important bit is the <code>paint()</code> method under <code>ProgressMeter</code> (first class): for every frame in the animation this example paints the background, the actual progress pie, and the percentage indicator. </p>
  
  <p>If I change the code to something like:</p>
  
  <pre><code>if self.angle &gt; 120:
      # do not draw background
  </code></pre>
  
  <p>then after 120 frames, the background does not get drawn anymore.</p>
  
  <p>This seems terribly inefficient because (logically) the background should only be drawn once, no? </p>
  
  <p>What would you recommend for animations like these?</p>
  
  <p>Addendum: I have lurked a lot on this site to steal examples and code, but haven't posted for a long time. Please let me know about proper etiquette etc if I am not following it properly.</p>
  
  <pre><code>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 &gt; 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_())
  </code></pre>