Python 在Kivy中旋转图像,而不使用Builder/.kv文件和NumericProperty

Python 在Kivy中旋转图像,而不使用Builder/.kv文件和NumericProperty,python,kivy,Python,Kivy,因此,基于一系列不同的示例,我拼凑了一个示例Kivy应用程序,它使用Kivy动画来旋转图像 我想知道如何在不使用.kv文件(或Builder.load\u string)的情况下获得相同的结果 但无法使用动画更新self.rot.angle。如果我手动处理动画,效果很好,但我想使用Kivy动画对象 有没有一种简单的python方法可以做到这一点 是的,这是可能的,这是您的Sprite类,它只使用python代码进行旋转。这里唯一的一件事是——kv lang的诞生是有原因的!这让这件事变得容易多了

因此,基于一系列不同的示例,我拼凑了一个示例Kivy应用程序,它使用Kivy
动画来旋转图像

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

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

有没有一种简单的python方法可以做到这一点


是的,这是可能的,这是您的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\u angle)
,它将在
self.angle
更改时调用
on\u angle
函数。您必须定义一个
self.on_angle
函数,该函数应重置
self.rot.angle=self.angle
。KV lang会自动为您执行此操作,为您节省大量代码。在kv中,
angle:self.angle
会在
self.angle
变量发生变化时自动更新
angle
变量,这意味着不玩
bind
功能

这是你的雪碧课。请注意两行
self.rot.origin=self.center
self.anim.start(self.rot)


谢谢你的回答。我没有想到
小部件.rot
可能是另一个小部件。我不喜欢
.kv
文件,因为它将“业务逻辑”从程序中分离出来,最终有两个地方定义了一个对象。这是两个需要编码/检查/修复/调试的地方。它与旧的MFC资源“.rc”文件、SQL存储过程相同—您的逻辑流会诱使定义不清和扩散。我觉得最好是让所有的代码都可读&在一个地方。如果这仅仅意味着多写几行代码,那么。。。这是一个很小的代价。
#! /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()
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