Data driven variables with kivy properties

Mathieu Virbel edited this page Sep 27, 2013 · 1 revision
Clone this wiki locally

Summary

When working in big apps, it can be useful to expose your widgets, and variables. This allows you to easily change the data for any of the widgets through a .kv file. Also, it allows non-programmers to make signifigant changes to the application, without having to dive into code!

The Problem

In our Kivy game, we have a cat object.

cat.py

#!Python
class Cat(Widget):
    mCatSpeed = NumericProperty(20)
    mCatMass = NumericProperty(50)

    def Update(self, dt):
        # Do physics calculations based on Speed and Mass

As you can see here, our python "cat" object is a Kivy Widget that has a few custom member variables: mCatSpeed and mCatMass.

What happens when you want to have 2 different cats, of different speeds? The larger cats go slower, the lighter cats go faster.

You could write code to generate these objects catFactory.py

#!Python
class CatFactory():
    def GenerateCats(inRootWidget):
        cat1 = Cat()
        cat1.mCatSpeed = 10
        cat1.mCatMass = 100
        inRootWidget.add_widget(cat1)

        cat2 = cat()
        # ...etc

But now every time you want to change the cats, you have to go into this code and change the varaibles, and copy a lot of code with different names.

What happens when you want cats in different areas? Things start to get messy.

Data Driven Solution

Kivy allows us to data drive our application, which is a super useful, super powerful solution.

Lets say our cat lives inside a room, lets make the 3 cats like this:

room.kv

#!text
<Room>:
    Cat:
        id: fatCat
        pos: 100, 100
        mCatSpeed: 10
        mCatMass: 100

    Cat:
        id: smallCat
        pos: 100, 50
        mCatSpeed: 50
        mCatMass: 25

Pretty handy, right? You can now just write your different cats with whatever data you want, assuming they are set up the way you need.

What's better is that non-programmers (Artists, Designers) can now create large portions of your applications much more rapidly than doing it through code!

The technical

For a variable to be settable, like this by kivy, it must be a kivy property.

#!python
class MyWidget(Widget):
    # Can be set in .kv files, since its a Kivy Property
    speed1 = NumericProperty(0)
    # Cannot be set, its not a Kivy Property
    speed2 = 0

The variable is a bit finicky with naming too. It must be lowercased first letter!

#!python
class MyWidget(Widget):
    # Works, as the first character is lowercased
    speed1 = NumericProperty(0)
    sPEEd2 = NumericProperty(0)
    # Does not work, as the first character is capitalized
    Speed3 = NumericProperty(0)

Sample Code

This sample is very basic, and shows how you can get the above results.

testWidget.py

#!python
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import StringProperty, NumericProperty, ObjectProperty
# This import is required for the TestWidet.kv file to know what a Cat is.
import cat

Builder.load_file("TestWidget.kv")

class TapApp(App):
    def build(self):
        return TestWidget()

class TestWidget(Widget):
    pass

if __name__ in ('__main__', '__android__'):
    TapApp().run()

TestWidget.kv

#!text
<TestWidget>:
    Cat:
        pos: 100, 100
        catSpeed: 30

cat.py

#!python
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import StringProperty, NumericProperty, ObjectProperty
from kivy.clock import Clock

Builder.load_file("Cat.kv")

class Cat(Widget):
    catSpeed = NumericProperty(20)

    def __init__(self, **kwargs):
        super(Cat, self).__init__(**kwargs)
        Clock.schedule_interval(self.Update, 1.0 / 60.0)

    def Update(self, dt):
        print(self.catSpeed)

Cat.kv

#!text
<Cat>: