Animating widgets as in our previous example of the snake game can be tyring. However, there is a built-in way available in kivy which allows you to quickly animate basic properties of your widgets.
The Animation Module
The built-in animation module provides basic functions to animate widget properties. The process is simple. Simply define the variable of a property, choose a duration for the transition between the current and new state and then start the animation.
A simple example to change the size of a widget would look like this
animation = Animation(background_color = color , duration= 2)
animation.start(widget)
App to test various animations
The following app allows you to test different animations in a little sand box.
Creating the layout in kv language
The layout is quickly created. The root layout is a FloatLayout which contains the sand box and the data entry section. The object we are going to create is a button which is identified by the id animatedButton. A Popup which is defined at the bottom of the kv file is used to choose a nice color which is used to change the background color of the button.
<AnimatedLayout>:
orientation: 'horizontal'
FloatLayout:
canvas.before:
Color:
rgba: (1,1,1,1)
Rectangle:
pos: self.pos
size: self.size
AnimatedButton:
id: animatedButton
pos_hint: {'center_x' : .5, 'center_y' : .5}
size_hint: .25,.25
BoxLayout:
size_hint: .25, 1
orientation: 'vertical'
Label:
text: 'Color'
Button:
text: 'choose a color'
on_press: root.choose_color()
Button:
text: 'Animate Color'
on_press: root.ids['animatedButton'].animateColor()
Label:
text: 'Size'
TextInput:
on_text: root.ids['animatedButton'].sizing = self.text
Button:
text: 'Animate Size'
on_press: root.ids['animatedButton'].animateSize()
Label:
text: 'Position'
GridLayout:
cols: 2
Label:
size_hint_x: .25
text: 'x'
TextInput:
on_text: root.ids['animatedButton'].xval = self.text
Label:
size_hint_x: .25
text: 'y'
TextInput:
on_text: root.ids['animatedButton'].yval = self.text
Button:
text: 'Animate Position'
on_press: root.ids['animatedButton'].animatePosition()
Button:
text: 'Animate all together'
on_press: root.ids['animatedButton'].animateAllParallel()
Button:
text: 'Animate all in sequence'
on_press: root.ids['animatedButton'].animateAllSequential()
<PopupColor>:
BoxLayout
orientation: 'vertical'
ColorPicker:
size_hint: 1, .75
on_color: app.root.myColor = self.color
Button:
size_hint: 1, .25
text: 'close me'
on_press: root.dismiss()
Defining the animations in the Python file
The App returns the root widget our AnimatedLayout. All the animations are defined directly in the class AnimatedButton.
Changing color
The function animateColor takes the selected color from the color picker and transitions it within two seconds.
Changing Size
The fuction animateSize transitions the size within five seconds. A helper function newSizeHint is used to calculate the new size_hint values.
Changing Position
And again in two seconds is the position changed in the function animatePosition. To make things a bit more interesting let's use the transition parameter. By setting it to t = in_out_back is the transition moving a bit faster in the middle and overshooting at the beginning of the motion.
The animation module offers all sorts of transitions. Feel free to play around with them.
The helper function newPosition converts the string values from the TextInput field to float. Please keep the values between 0 and 1. The coordinate system starts at the lower left corner of the sand box. x = 0.25 and y = 0.25 will position the button in the lower left corner of the sand box.
Some extras
You can run animations one after the other by joining the using the + operator or run them all in parallel by joining them via the & operator. The functions animateAllParallel and animateAllSequential demonstrate this.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.animation import Animation
from kivy.uix.popup import Popup
from kivy.properties import ListProperty, NumericProperty, StringProperty
class AnimateApp (App):
def __init__(self, **kwargs):
super(AnimateApp, self).__init__(**kwargs)
Window.size = 1200, 700
def build(self):
return AnimatedLayout()
class AnimatedLayout (BoxLayout):
myColor = ListProperty([1,1,1,1])
def choose_color(self):
popup = PopupColor(title='Choose a color')
popup.open()
class AnimatedButton (Button):
sizing = StringProperty()
xval = StringProperty()
yval = StringProperty()
new_x = NumericProperty()
nex_y = NumericProperty()
def newSizeHint(self):
sizeFactor = float(self.sizing)
return self.size_hint_x * sizeFactor, self.size_hint_y * sizeFactor
def newPosition(self):
self.new_x = float(self.xval)
self.new_y = float(self.yval)
def animateColor(self):
color = App.get_running_app().root.myColor
animation = Animation(background_color = color , duration= 2)
animation.start(self)
def animateSize(self):
animation = Animation(size_hint = self.newSizeHint(), duration = 5)
animation.start(self)
def animatePosition(self):
self.newPosition()
animation = Animation(pos_hint = {'center_x': self.new_x, 'center_y': self.new_y}, duration = 2, t = 'in_out_back')
animation.start(self)
def animateAllParallel(self):
color = App.get_running_app().root.myColor
self.newPosition()
animation = Animation(background_color=color, duration=2) &\
Animation(size_hint = self.newSizeHint(), duration = 5) &\
Animation(pos_hint={'center_x': self.new_x, 'center_y': self.new_y}, duration=2)
animation.start(self)
def animateAllSequential(self):
color = App.get_running_app().root.myColor
self.newPosition()
animation = Animation(background_color=color, duration=2) +\
Animation(size_hint = self.newSizeHint(), duration = 5) +\
Animation(pos_hint={'center_x': self.new_x, 'center_y': self.new_y}, duration=2)
animation.start(self)
class PopupColor (Popup):
pass
if __name__ == '__main__':
AnimateApp().run()
What now?
There are some more parameters in the animation module with which you should play around. E.g. keep animations running forever or stop them on purpose.
You might as well improve the helper functions to make them error proof.