所以我试图在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_())
答案 0 :(得分:0)
Qt关于QWidget重绘插槽的文档说:
通过立即调用paintEvent()直接重新绘制窗口小部件,除非禁用更新或隐藏窗口小部件。 如果您需要立即重新绘制,我们建议仅使用repaint(),例如在动画期间。在几乎所有情况下,update()都更好,因为它允许Qt优化速度并最大限度地减少闪烁。 警告:如果在可能从paintEvent()调用的函数中调用repaint(),则可能会获得无限递归。 update()函数永远不会导致递归。
这应该会给你一个关于何时使用或不重新绘制或更新插槽的答案。
关于制作动画我建议您也要查看Qt4's animation framework或Qt5's animation framework,这是在Qt上制作小部件动画的一种非常强大的方法。