2016-08-03 94 views
0

所以我試圖學習PyQt中的動畫以及我可以在網上找到的所有例子,他們似乎都使用self.update()self.repaint()方法來增加動畫。這意味着基本上,代碼必須擦除然後重新繪製每個幀的整個小部件,即使我打算製作動畫的很多部分都是靜態的。PyQt - 動畫沒有重繪一切?

例如下面的代碼,這會生成一個循環進度餅圖。重要的一點是ProgressMeter(第一類)下的paint()方法:對於動畫中的每個幀,此示例都繪製背景,實際進度餅圖和百分比指示符。

如果我的代碼更改爲類似:

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

回答

0

的Qt的QWidget的左右槽重繪文檔說:

重繪直接的窗口小部件通過調用的paintEvent()立即,除非更新被禁用或小部件是隱藏的。 如果需要立即重繪,例如在動畫過程中,我們建議只使用repaint()。在幾乎所有情況下,update()都更好,因爲它允許Qt優化速度並最大限度地減少閃爍。 警告:如果您在可能自己從paintEvent()調用的函數中調用repaint(),則可能會獲得無限遞歸。 update()函數從不會導致遞歸。

這應該給你一個關於何時使用或不重新刷新或更新插槽的答案。

關於製作動畫我建議你也看看Qt4's animation frameworkQt5's animation framework,這是一個非常強大的在Qt上動畫插件的強大方式。

+0

謝謝!那個文檔就是我之前看到的提示這個問題的東西。問題就像它說的那樣,兩種方法都會擦除然後重新繪製整個小部件,但我真的不需要這樣做,因爲只有所有對象中的一小部分需要更改。例如在Tkinter中(我正在從GUI遷移GUI),我可以創建一個畫布,但只需使用self.object.move()方法只移動我需要的動畫。再次感謝! – user3627191

+0

@ user3627191不客氣,但你沒有把答案標記爲已接受,還有什麼還不清楚嗎? – BPL

+0

我的壞 - upvoted,但沒有意識到我需要點擊複選標記以及:) – user3627191