uclatommy
uclatommy

Reputation: 315

How do I set widget attributes by calling a function in Kivy Python?

Suppose I have a ThemeManager object as a class attribute in my RootWidget like so:

class RootWidget(Widget):
    theme = ThemeManager()

The ThemeManager defines a function that returns a hex color.

class ThemeManager:    
    def get_color(self):
        return '#ffffffff'

Let's say I create a Button in my RootWidget using a kv file. How would I be able to call the ThemeManager functions from the kv file? Here's an example that doesn't work:

import kivy
kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.lang import Builder


class ThemeManager:
    def get_color(self):
        return '#ffffffff'


class RootWidget(Widget):
    theme = ThemeManager()


my_kv = Builder.load_string("""
#: import get_color_from_hex kivy.utils.get_color_from_hex
RootWidget:
    Button:
        color: get_color_from_hex(app.root.theme.get_color())
        text: "Test"
""")


class TestApp(App):
    def build(self):
        return my_kv

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

Upvotes: 1

Views: 2237

Answers (1)

Ng Oon-Ee
Ng Oon-Ee

Reputation: 1253

Since your question is already answered, here's a stab at the explanation, it's actually pretty simple (I think).

app.root is None at the point where your Button is trying to read the function. Because the order of things is (loosely):-

  1. RootWidget created
  2. Once it and all it's children are done (init completed), the object gets passed to the line in build()
  3. app.root is only set on the call to TestApp.run()

As to why 3. happens, the init method in app.py initializes self.root as None. It can then be set by load_kv (loads a kv with the same name as this app) or by run (which is what happens most of the time).

So you can call app.root in your on_press events (because these only happen in response to user interaction, when the app is fully created) but not in one-off widget initialization events.

Interestingly enough, root is not defined as an ObjectProperty in app.py, which means you can't bind to changes in it like you can with, say, the title and icon. Not sure if it'd ever change though, so this is probably moot.

Upvotes: 1

Related Questions