专栏首页Python编程 pyqt matplotlibPyQt5 动画类--跳舞的火柴人

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

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

本文分享自微信公众号 - Python编程 pyqt matplotlib(wsplovePython),作者:Riverbank

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • PyQt 中心窗口、停靠窗口和状态栏

    self.setCentralWidget(widget)#self是主窗口子类的实例

    用户6021899
  • PyQt5 非模态对话框(apply 型)

    如果希望用户可以重复更改对话框中的参数并能马上看到修改结果,那么就要使用非模态对话框,这样用户就可以按照他们喜欢的方式来持续不断地修改数据并验证修改的结果了。

    用户6021899
  • 箱线图(BoxPlot) App

    由于公司的Execl版本(v2010)偏低,没有画箱线图的功能,故我用python写了一小段程序,可以用来画箱线图。绘图库使用的还是matplotlib。

    用户6021899
  • day23 03 组合的例子

    对象=类名()----实例化过程:创建了一个self对象,执行_init_方法初始化,返回self对象给外部

    py3study
  • Python写的俄罗斯方块

    简单瞅了下Tkinter,和Canvas配合在一起,还算是简洁的界面开发API。threading.Thread创建新的线程,其多线程机制也算是方便。

    py3study
  • python pyqt5仿window任务计划程序

    from PyQt5 import QtCore, QtWidgets import sys,os import win32api import win3...

    用户5760343
  • selenium爬取拉勾网python职位信息

    菲宇
  • unslider源码分析

    根据Bootstrap中文网的介绍,Unslider一个超小的 jQuery轮播(slider)插件,参照这个汉化版的介绍页面,这个插件有你需要的优点,但是本...

    用户3579639
  • MJRefresh源码剖析与学习

    建议查看原文:https://www.jianshu.com/p/23c876f8ae39(不定时更新)

    Dwyane
  • pyqt5实现浏览器与下载文件弹框

    本文由腾讯云+社区自动同步,原文地址 https://stackoverflow.club/article/pyqt5_webbrowser_download_...

    羽翰尘

扫码关注云+社区

领取腾讯云代金券