首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PyQt5 动画类--跳舞的火柴人

PyQt5 动画类--跳舞的火柴人

作者头像
用户6021899
发布2019-11-14 17:08:36
1.5K0
发布2019-11-14 17:08:36
举报

PyQt5.QtCore中的 QPropertyAnimation可以实现动画功能。

下面第一个例子通过将一个QLabel对象移动和放大来实现简单的动画:

import sysfrom PyQt5.QtCore import QPropertyAnimation, QRect, QEasingCurvefrom PyQt5 import QtGuifrom PyQt5.QtWidgets import QApplication, QWidget, QLabel
class AnimationDemo(QWidget):    def __init__(self):        super().__init__()        self.resize(800, 800)        self.label = QLabel('Move', self)        pixmap = QtGui.QPixmap('sight.jpg')        self.label.setPixmap(pixmap)        self.label.setGeometry(0, 0, 300, 300)        self.animation = QPropertyAnimation(self.label, b'geometry')         # 实例化一个动画对象          # 参数1  动画要操作的对象          # 参数2  要改变的属性     geometry位置和大小                    #注意:字节类型                    #pos---位置动画---QPoint                    #size---大小动画---QSize                    #geometry----位置+大小动画----QRect                    #windowOpacity---窗口的透明度(0.0是透明的    1.0是不透明)        self.animation.setDuration(5000)  # 设置动画持续时间。单位毫秒        self.animation.setStartValue(QRect(0, 0, 300, 300))  #动画开始时的位置和大小        self.animation.setEndValue(QRect(500, 500, 500, 500))  #动画结束时的位置和大小        #self.animation.setEasingCurve(QEasingCurve.InBounce) #设置缓和曲线        self.animation.setLoopCount(-1)  #设置循环次数            #-1  无限循环            #0  不循环            #正数   循环次数                self.animation.start()#动画开始        if __name__ == "__main__":    app = QApplication(sys.argv)    demo = AnimationDemo()    demo.show()    sys.exit(app.exec_())

第二个例子来自PyQt4官方demo(现已升级为PyQt5版本)。代码实现了一个火柴人,他开心时可以跳跃、可以舞蹈,不开心时可以躺地上……

代码有点复杂:

#!/usr/bin/env python################################################################################# Copyright (C) 2010 Riverbank Computing Limited.## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).## All rights reserved.

import mathfrom PyQt5 import QtCore, QtWidgets, QtGuiimport stickman_qrc
class Node(QtWidgets.QGraphicsObject):    positionChanged = QtCore.pyqtSignal()    def __init__(self, pos, parent=None):        super().__init__(parent)        self.m_dragging = False        self.setPos(pos)        self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges)    def boundingRect(self):        return QtCore.QRectF(-6.0, -6.0, 12.0, 12.0)    def paint(self, painter, option, widget):        painter.setPen(QtCore.Qt.white)        painter.drawEllipse(QtCore.QPointF(0.0, 0.0), 5.0, 5.0)    def itemChange(self, change, value):        if change == QtWidgets.QGraphicsItem.ItemPositionChange:            self.positionChanged.emit()        return super(Node, self).itemChange(change, value)    def mousePressEvent(self, event):        self.m_dragging = True    def mouseMoveEvent(self, event):        if self.m_dragging:            self.setPos(self.mapToParent(event.pos()))    def mouseReleaseEvent(self, event):        self.m_dragging = False
Coords = (    # Head: 0    (0.0, -150.0),    # Body pentagon, top->bottom, left->right: 1 - 5    (0.0, -100.0),    (-50.0, -50.0),    (50.0, -50.0),    (-25.0, 50.0),    (25.0, 50.0),    # Right arm: 6 - 7    (-100.0, 0.0),    (-125.0, 50.0),    # Left arm: 8 - 9    (100.0, 0.0),    (125.0, 50.0),    # Lower body: 10 - 11    (-35.0, 75.0),    (35.0, 75.0),    # Right leg: 12 - 13    (-25.0, 200.0),    (-30.0, 300.0),    # Left leg: 14 - 15    (25.0, 200.0),    (30.0, 300.0))
Bones = (    # Neck.    (0, 1),    # Body.    (1, 2),    (1, 3),    (1, 4),    (1, 5),    (2, 3),    (2, 4),    (2, 5),    (3, 4),    (3, 5),    (4, 5),    # Right arm.    (2, 6),    (6, 7),    # Left arm.    (3, 8),    (8, 9),    # Lower body.    (4, 10),    (4, 11),    (5, 10),    (5, 11),    (10, 11),    # Right leg.    (10, 12),    (12, 13),    # Left leg.    (11, 14),    (14, 15))
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* class StickMan(QtWidgets.QGraphicsObject):    def __init__(self):        super().__init__()        self.m_sticks = True        self.m_isDead = False        self.m_pixmap = QtGui.QPixmap('images/head.png')        self.m_penColor = QtGui.QColor(QtCore.Qt.white)        self.m_fillColor = QtGui.QColor(QtCore.Qt.black)        # Set up start position of limbs.        self.m_nodes = []        for x, y in Coords:            node = Node(QtCore.QPointF(x, y), self)            node.positionChanged.connect(self.childPositionChanged)            self.m_nodes.append(node)        self.m_perfectBoneLengths = []        for n1, n2 in Bones:            node1 = self.m_nodes[n1]            node2 = self.m_nodes[n2]            dist = node1.pos() - node2.pos()            self.m_perfectBoneLengths.append(math.hypot(dist.x(), dist.y()))        self.startTimer(10)    def childPositionChanged(self):        self.prepareGeometryChange()    def setDrawSticks(self, on):        self.m_sticks = on        for node in self.m_nodes:            node.setVisible(on)    def drawSticks(self):        return self.m_sticks    def boundingRect(self):        # Account for head radius of 50.0 plus pen which is 5.0.        return self.childrenBoundingRect().adjusted(-55.0, -55.0, 55.0, 55.0)    def nodeCount(self):        return len(self.m_nodes)    def node(self, idx):        if idx >= 0 and idx < len(self.m_nodes):            return self.m_nodes[idx]        return None    def timerEvent(self, e):        self.update()    def stabilize(self):        threshold = 0.001        for i, (n1, n2) in enumerate(Bones):            node1 = self.m_nodes[n1]            node2 = self.m_nodes[n2]            pos1 = node1.pos()            pos2 = node2.pos()            dist = pos1 - pos2            length = math.hypot(dist.x(), dist.y())            diff = (length - self.m_perfectBoneLengths[i]) / length            p = dist * (0.5 * diff)            if p.x() > threshold and p.y() > threshold:                pos1 -= p                pos2 += p                node1.setPos(pos1)                node2.setPos(pos2)    def posFor(self, idx):        return self.m_nodes[idx].pos()    @QtCore.pyqtProperty(QtGui.QColor)    def penColor(self):        return QtGui.QColor(self.m_penColor)    @penColor.setter    def penColor(self, color):        self.m_penColor = QtGui.QColor(color)    @QtCore.pyqtProperty(QtGui.QColor)    def fillColor(self):        return QtGui.QColor(self.m_fillColor)    @fillColor.setter    def fillColor(self, color):        self.m_fillColor = QtGui.QColor(color)    @QtCore.pyqtProperty(bool)    def isDead(self):        return self.m_isDead    @isDead.setter    def isDead(self, isDead):        self.m_isDead = isDead    def paint(self, painter, option, widget):        self.stabilize()               if self.m_sticks:            painter.setPen(QtCore.Qt.white)            for n1, n2 in Bones:                node1 = self.m_nodes[n1]                node2 = self.m_nodes[n2]                painter.drawLine(node1.pos(), node2.pos())        else:            # First bone is neck and will be used for head.            path = QtGui.QPainterPath()            path.moveTo(self.posFor(0))            path.lineTo(self.posFor(1))            # Right arm.            path.lineTo(self.posFor(2))            path.lineTo(self.posFor(6))            path.lineTo(self.posFor(7))            # Left arm.            path.moveTo(self.posFor(3))            path.lineTo(self.posFor(8))            path.lineTo(self.posFor(9))            # Body.            path.moveTo(self.posFor(2))            path.lineTo(self.posFor(4))            path.lineTo(self.posFor(10))            path.lineTo(self.posFor(11))            path.lineTo(self.posFor(5))            path.lineTo(self.posFor(3))            path.lineTo(self.posFor(1))            # Right leg.            path.moveTo(self.posFor(10))            path.lineTo(self.posFor(12))            path.lineTo(self.posFor(13))            # Left leg.            path.moveTo(self.posFor(11))            path.lineTo(self.posFor(14))            path.lineTo(self.posFor(15))            painter.setPen(QtGui.QPen(self.m_penColor, 5.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap))            painter.drawPath(path)            n1, n2 = Bones[0]            node1 = self.m_nodes[n1]            node2 = self.m_nodes[n2]            dist = node2.pos() - node1.pos()            sinAngle = dist.x() / math.hypot(dist.x(), dist.y())            angle = math.degrees(math.asin(sinAngle))            headPos = node1.pos()            painter.translate(headPos)            painter.rotate(-angle)            painter.setBrush(self.m_fillColor)            painter.drawEllipse(QtCore.QPointF(0, 0), 50.0, 50.0)            painter.setBrush(self.m_penColor)            painter.setPen(QtGui.QPen(self.m_penColor, 2.5, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap))            # Eyes.            if self.m_isDead:                painter.drawLine(-30.0, -30.0, -20.0, -20.0)                painter.drawLine(-20.0, -30.0, -30.0, -20.0)                painter.drawLine(20.0, -30.0, 30.0, -20.0)                painter.drawLine(30.0, -30.0, 20.0, -20.0)            else:                painter.drawChord(QtCore.QRectF(-30.0, -30.0, 25.0, 70.0), 30.0 * 16, 120.0 * 16)                painter.drawChord(QtCore.QRectF(5.0, -30.0, 25.0, 70.0), 30.0 * 16, 120.0 * 16)            # Mouth.            if self.m_isDead:                painter.drawLine(-28.0, 2.0, 29.0, 2.0)            else:                painter.setBrush(QtGui.QColor(128, 0, 64 ))                painter.drawChord(QtCore.QRectF(-28.0, 2.0 - 55.0 / 2.0, 57.0, 55.0), 0.0, -180.0 * 16)            # Pupils.            if not self.m_isDead:                painter.setPen(QtGui.QPen(self.m_fillColor, 1.0, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap))                painter.setBrush(self.m_fillColor)                painter.drawEllipse(QtCore.QPointF(-12.0, -25.0), 5.0, 5.0)                painter.drawEllipse(QtCore.QPointF(22.0, -25.0), 5.0, 5.0)
*/
class GraphicsView(QtWidgets.QGraphicsView):    keyPressed = QtCore.pyqtSignal(int)    def keyPressEvent(self, e):        if e.key() == QtCore.Qt.Key_Escape:            self.close()        self.keyPressed.emit(QtCore.Qt.Key(e.key()))
class Frame(object):       def __init__(self):        self.m_nodePositions = []    def nodeCount(self):        return len(self.m_nodePositions)    def setNodeCount(self, nodeCount):        while nodeCount > len(self.m_nodePositions):            self.m_nodePositions.append(QtCore.QPointF())        while nodeCount < len(self.m_nodePositions):            self.m_nodePositions.pop()    def nodePos(self, idx):        return QtCore.QPointF(self.m_nodePositions[idx])    def setNodePos(self, idx, pos):        self.m_nodePositions[idx] = QtCore.QPointF(pos)
class Animation(object):    def __init__(self):        self.m_currentFrame = 0        self.m_frames = [Frame()]        self.m_name = ''    def setTotalFrames(self, totalFrames):        while len(self.m_frames) < totalFrames:            self.m_frames.append(Frame())        while totalFrames < len(self.m_frames):            self.m_frames.pop()    def totalFrames(self):        return len(self.m_frames)    def setCurrentFrame(self, currentFrame):        self.m_currentFrame = max(min(currentFrame, self.totalFrames() - 1), 0)    def currentFrame(self):        return self.m_currentFrame    def setNodeCount(self, nodeCount):        frame = self.m_frames[self.m_currentFrame]        frame.setNodeCount(nodeCount)    def nodeCount(self):        frame = self.m_frames[self.m_currentFrame]        return frame.nodeCount()    def setNodePos(self, idx, pos):        frame = self.m_frames[self.m_currentFrame]        frame.setNodePos(idx, pos)    def nodePos(self, idx):        frame = self.m_frames[self.m_currentFrame]        return frame.nodePos(idx)    def name(self):        return self.m_name    def setName(self, name):        self.m_name = name    def save(self, device):        stream = QtCore.QDataStream(device)        stream.writeQString(self.m_name)        stream.writeInt(len(self.m_frames))        for frame in self.m_frames:            stream.writeInt(frame.nodeCount())            for i in range(frame.nodeCount()):                stream << frame.nodePos(i)    def load(self, device):        self.m_frames = []        stream = QtCore.QDataStream(device)        self.m_name = stream.readQString()        frameCount = stream.readInt()        for i in range(frameCount):            nodeCount = stream.readInt()            frame = Frame()            frame.setNodeCount(nodeCount)            for j in range(nodeCount):                pos = QtCore.QPointF()                stream >> pos                frame.setNodePos(j, pos)            self.m_frames.append(frame)
class KeyPressTransition(QtCore.QSignalTransition):    def __init__(self, receiver, key, target=None):        super(KeyPressTransition, self).__init__(receiver.keyPressed)        self.m_key = key        if target is not None:            self.setTargetState(target)    def eventTest(self, e):        if super(KeyPressTransition, self).eventTest(e):            key = e.arguments()[0]            return key == self.m_key        return False
class LightningStrikesTransition(QtCore.QEventTransition):    def __init__(self, target):        super().__init__()        self.setEventSource(self)        self.setEventType(QtCore.QEvent.Timer)        self.setTargetState(target)        QtCore.qsrand(QtCore.QDateTime.currentDateTime().toTime_t())        self.startTimer(1000)    def eventTest(self, e):        return (super(LightningStrikesTransition, self).eventTest(e) and                (QtCore.qrand() % 50) == 0)
class LifeCycle(object):    def __init__(self, stickMan, keyReceiver):        self.m_stickMan = stickMan        self.m_keyReceiver = keyReceiver        # Create animation group to be used for all transitions.        self.m_animationGroup = QtCore.QParallelAnimationGroup()        stickManNodeCount = self.m_stickMan.nodeCount()        self._pas = []        for i in range(stickManNodeCount):            pa = QtCore.QPropertyAnimation(self.m_stickMan.node(i), b'pos')            self._pas.append(pa)            self.m_animationGroup.addAnimation(pa)                   # Set up intial state graph.        self.m_machine = QtCore.QStateMachine()        self.m_machine.addDefaultAnimation(self.m_animationGroup)        self.m_alive = QtCore.QState(self.m_machine)        self.m_alive.setObjectName('alive')        # Make it blink when lightning strikes before entering dead animation.        lightningBlink = QtCore.QState(self.m_machine)        lightningBlink.assignProperty(self.m_stickMan.scene(),'backgroundBrush', QtCore.Qt.white)        lightningBlink.assignProperty(self.m_stickMan, 'penColor',QtCore.Qt.black)        lightningBlink.assignProperty(self.m_stickMan, 'fillColor',QtCore.Qt.white)        lightningBlink.assignProperty(self.m_stickMan, 'isDead', True)        timer = QtCore.QTimer(lightningBlink)        timer.setSingleShot(True)        timer.setInterval(100)        lightningBlink.entered.connect(timer.start)        lightningBlink.exited.connect(timer.stop)        self.m_dead = QtCore.QState(self.m_machine)        self.m_dead.assignProperty(self.m_stickMan.scene(), 'backgroundBrush',QtCore.Qt.black)        self.m_dead.assignProperty(self.m_stickMan, 'penColor',QtCore.Qt.white)        self.m_dead.assignProperty(self.m_stickMan, 'fillColor',QtCore.Qt.black)        self.m_dead.setObjectName('dead')        # Idle state (sets no properties).        self.m_idle = QtCore.QState(self.m_alive)        self.m_idle.setObjectName('idle')        self.m_alive.setInitialState(self.m_idle)        # Lightning strikes at random.        self.m_alive.addTransition(LightningStrikesTransition(lightningBlink))        lightningBlink.addTransition(timer.timeout, self.m_dead)        self.m_machine.setInitialState(self.m_alive)
    def setDeathAnimation(self, fileName):        deathAnimation = self.makeState(self.m_dead, fileName)        self.m_dead.setInitialState(deathAnimation)    def start(self):        self.m_machine.start()    def addActivity(self, fileName, key):        state = self.makeState(self.m_alive, fileName)        self.m_alive.addTransition(KeyPressTransition(self.m_keyReceiver, key, state))    def makeState(self, parentState, animationFileName):        topLevel = QtCore.QState(parentState)        animation = Animation()        file = QtCore.QFile(animationFileName)        if file.open(QtCore.QIODevice.ReadOnly):            animation.load(file)        frameCount = animation.totalFrames()        previousState = None        for i in range(frameCount):            animation.setCurrentFrame(i)            frameState = QtCore.QState(topLevel)            nodeCount = animation.nodeCount()            for j in range(nodeCount):                frameState.assignProperty(self.m_stickMan.node(j), b'pos',                        animation.nodePos(j))            frameState.setObjectName('frame %d' % i)            if previousState is None:                topLevel.setInitialState(frameState)            else:                previousState.addTransition(previousState.propertiesAssigned,                        frameState)            previousState = frameState        previousState.addTransition(previousState.propertiesAssigned,                topLevel.initialState())        return topLevel
if __name__ == '__main__':    import sys    app = QtWidgets.QApplication(sys.argv)    stickMan = StickMan()    stickMan.setDrawSticks(False)    textItem = QtWidgets.QGraphicsTextItem()    textItem.setHtml("<font color=\"white\"><b>Stickman</b>"        "<p>"        "Tell the stickman what to do!"        "</p>"        "<p><i>"        "<li>Press <font color=\"purple\">J</font> to make the stickman jump.</li>"        "<li>Press <font color=\"purple\">D</font> to make the stickman dance.</li>"        "<li>Press <font color=\"purple\">C</font> to make him chill out.</li>"        "<li>When you are done, press <font color=\"purple\">Escape</font>.</li>"        "</i></p>"        "<p>If he is unlucky, the stickman will get struck by lightning, and never jump, dance or chill out again."        "</p></font>")    w = textItem.boundingRect().width()    stickManBoundingRect = stickMan.mapToScene(stickMan.boundingRect()).boundingRect()    textItem.setPos(-w / 2.0, stickManBoundingRect.bottom() + 25.0)    scene = QtWidgets.QGraphicsScene()    scene.addItem(stickMan)    scene.addItem(textItem)    scene.setBackgroundBrush(QtCore.Qt.black)    view = GraphicsView()    view.setRenderHints(QtGui.QPainter.Antialiasing)    view.setTransformationAnchor(QtWidgets.QGraphicsView.NoAnchor)    view.setScene(scene)    view.show()    view.setFocus()       # Make enough room in the scene for stickman to jump and die.    sceneRect = scene.sceneRect()    view.resize(sceneRect.width() + 100, sceneRect.height() + 100)    view.setSceneRect(sceneRect)    cycle = LifeCycle(stickMan, view)    cycle.setDeathAnimation(':/animations/dead')    cycle.addActivity(':/animations/jumping', QtCore.Qt.Key_J)    cycle.addActivity(':/animations/dancing', QtCore.Qt.Key_D)    cycle.addActivity(':/animations/chilling', QtCore.Qt.Key_C)    cycle.start()        sys.exit(app.exec_())
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python可视化编程机器学习OpenCV 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档