在不使用Builder / .kv文件和NumericProperty的情况下在Kivy中旋转图像

时间:2019-05-21 06:13:27

标签: python kivy

因此,基于许多不同的示例,我将使用Kivy Animation旋转图像的示例Kivy应用拼凑在一起。

我想知道如何在不使用.kv文件(或Builder.load_string)的情况下获得相同的结果。

#! /usr/bin/env python3
import kivy
kivy.require('1.9.1')

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Rectangle, Color, Rotate, PushMatrix, PopMatrix
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.graphics.svg import Svg
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.properties import NumericProperty

import random

WINDOW_WIDTH, WINDOW_HEIGHT = Window.size

Builder.load_string('''
<Sprite>:
    canvas.before:
        PushMatrix
        Rotate:
            angle: self.angle
            axis: (0, 0, 1)
            origin: self.center
    canvas.after:
        PopMatrix
''')


class Sprite( Image ):
    angle = NumericProperty(0)

    def __init__( self, x=0, y=0, **kwargs ):
        super( Sprite, self ).__init__( **kwargs )

        self.size_hint = (None, None)  # tell the layout not to size me
        self.angle     = 0
        self.source    = 'alien.png'
        self.size      = self.texture.size 

        if ( x == 0 and y == 0 ):
            self.pos   = ( random.randrange(0,WINDOW_WIDTH) , random.randrange(0,WINDOW_HEIGHT) )
        else:
            self.pos   = ( x,y )

        self.animate() # start moving animation

    def animateComplete( self, *kargs ):
        Animation.cancel_all( self ) # is this needed?
        self.angle = 0
        self.animate()

    def animate( self ):
        self.anim = Animation( angle=360, duration=1 )
        self.anim.bind( on_complete=self.animateComplete )
        self.anim.repeat = True
        self.anim.start( self )


class FPSText( Label ):
    def __init__( self, **kwargs ):
        super( FPSText, self ).__init__( **kwargs )
        self.size_hint = (None, None)  # tell the layout not to size me
        self.pos_hint = { 'right':1, 'top':1 }

    def update( self, count ):
        self.text = "%d aliens / %3.1f FPS" % ( count, Clock.get_fps() ) 
        self.size = self.texture_size


class AlienGame( FloatLayout ):
    def __init__(self, **kwargs):
        super( AlienGame, self).__init__(**kwargs)
        self.aliens = []
        self.fps_text = FPSText()
        self.add_widget( self.fps_text )
        self.addAlien( WINDOW_WIDTH//2, WINDOW_HEIGHT//2 )

    def addAlien( self, x=0, y=0 ):
        new_alien = Sprite( x, y )
        self.aliens.append( new_alien )
        self.add_widget( new_alien )

    def update( self, dt ):
        self.fps_text.text = '--'
        self.fps_text.update( len( self.aliens ) )

    def on_touch_down( self, touch ):
        if ( touch.is_double_tap ):
            for i in range( 7 ):
                self.addAlien()
        else: #if ( touch.is_single_tap ):  (no single tap property)
            self.addAlien( touch.pos[0], touch.pos[1]  )

class RotApp( App ):
    def build( self ):
        game = AlienGame()
        Clock.schedule_interval(game.update, 1.0 / 60.0)
        return game


if ( __name__ == '__main__' ):    
    RotApp().run()

我尝试过类似的事情:

class Sprite( Image ):
    def __init__( self, x=0, y=0, **kwargs ):
        super( Sprite, self ).__init__( **kwargs )

        self.size_hint = (None, None)  # tell the layout not to size me
        self.source    = 'alien.png'
        self.size      = self.texture.size

        # define the rotation
        with self.canvas.before:
            PushMatrix()
            self.rot = Rotate()
            self.rot.angle  = 0
            self.rot.origin = self.center
            self.rot.axis = (0, 0, 1)
        with self.canvas.after:
            PopMatrix()

但是无法获得self.rot.angle来更新动画。如果我手动处理动画,效果很好,但是我想使用Kivy Animation对象。

是否有执行此操作的纯Python方法?

example output

1 个答案:

答案 0 :(得分:1)

是的,有可能,这是您的Sprite类,仅使用python代码进行旋转。这里唯一的是-制作kv lang是有原因的!它使这些工作变得容易得多。我强烈建议保留kv代码。无论如何,要回答您的问题:关键是要 1)在self.rot小部件而不是self小部件上启动动画 2)重置self.rot.origin变量,因为在初始化小部件时(以及在将其实际放置在屏幕上之前,将self.center设置为(0,0),为它提供适当的self.center)。

或者,您可以使用bind方法。例如,设置self.rot.angle = self.angle,然后再设置self.bind(angle=on_angle),它会在on_angle更改时随时调用self.angle函数。您将必须定义一个self.on_angle函数,该函数应重置self.rot.angle = self.angle。 KV lang自动为您执行此操作,从而节省了大量代码。在kv中,angle: self.angle变量更改时,angle会自动更新self.angle变量,这意味着无法使用bind函数。

这是您的Sprite类别。注意两行self.rot.origin = self.centerself.anim.start(self.rot)

class Sprite( Image ):
    angle = NumericProperty(0)

    def __init__( self, x=0, y=0, **kwargs ):
        super( Sprite, self ).__init__( **kwargs )
        # define the rotation
        with self.canvas.before:
            PushMatrix()
            self.rot = Rotate()
            self.rot.angle  = 0
            self.rot.origin = self.center
            self.rot.axis = (0, 0, 1)
        with self.canvas.after:
            PopMatrix()

        self.size_hint = (None, None)  # tell the layout not to size me
        self.angle     = 0
        self.source    = 'alien.png'
        self.size      = self.texture.size 

        if ( x == 0 and y == 0 ):
            self.pos   = ( random.randrange(0,WINDOW_WIDTH) , random.randrange(0,WINDOW_HEIGHT) )
        else:
            self.pos   = ( x,y )

        self.rot.origin = self.center # Reset the center of the Rotate canvas instruction
        self.animate() # start moving animation

    def animateComplete( self, *kargs ):
        Animation.cancel_all( self ) # is this needed?
        self.rot.angle = 0
        self.animate()

    def animate( self ):
        self.anim = Animation( angle=360, duration=1 )
        self.anim.bind( on_complete=self.animateComplete )
        self.anim.repeat = True
        self.anim.start( self.rot ) # Start rotating the self.rot widget instead of the self widget