Lorenz
Lorenz

Reputation: 35

How to pass properties from one class to another in kivy

In my application, I have multiple screens. Each screen corresponds to a class. Now I would like to display a property that was calculated in a certain class (screen) on another screen. Below is a simplified example to outline my problem. In the class WordComprehension, the NumericProperty count_r is incremented, every time the button is pressed. Now, I would like to display the result of this calculation in the ScoreScreen. Thanks for your suggestions. Here is the .py:

import kivy
from kivy.app import App

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.button import Button

from kivy.properties import ObjectProperty
from kivy.properties import NumericProperty


class AppScreen(FloatLayout):
    app = ObjectProperty(None)

class MainMenu(AppScreen):
    pass

class ScoreScreen(AppScreen):

    score = NumericProperty(0)
    def get_score(self):
        wordcomp = WordComprehension()
        self.score = wordcomp.count_r


class WordComprehension(AppScreen):
    count_r = NumericProperty(0)
    count_w = NumericProperty(0)

    def do_something(self):
        self.count_r += 1


class InterfaceApp(App):
    def build(self):
        self.screens = {}
        self.screens["wordcomp"] = WordComprehension(app=self)
        self.screens["menu"] = MainMenu(app=self)
        self.screens["score"] = ScoreScreen(app=self)
        self.root = FloatLayout()
        self.goto_screen("menu")
        return self.root

    def goto_screen(self, screen_name):
        self.root.clear_widgets()
        self.root.add_widget(self.screens[screen_name])


if __name__ == "__main__":
    InterfaceApp().run()

and the .kv:

#:kivy 1.8.0

<MainMenu>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: "Choose Category"
            font_size: 30 
        Button:
            text: 'Word Comprehension'
            on_press: root.app.goto_screen("wordcomp")
        Button:
            text: 'Highscore'
            on_press: root.app.goto_screen("score")
            disabled: False

<ScoreScreen>:
    BoxLayout:
        orientation: 'vertical'
        Button:
            text: 'get score'
            on_press: root.get_score()
        Label:
            text: "Word Comprehension right answers:" + str(root.score)
        Button:
            text: 'Main Menu'
            on_release: root.app.goto_screen("menu")


<WordComprehension>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: str(root.count_r)
        Button:
            text: 'Do something'
            on_release: root.do_something()
        Button:
            text: 'Menu'
            on_press: root.app.goto_screen("menu")

Upvotes: 2

Views: 4503

Answers (1)

Lobsterman
Lobsterman

Reputation: 256

You can bind kivy properties to one another, so that when one changes so does the other. To do this you bind to the setter() function of the class. (http://kivy.org/docs/api-kivy.event.html)

Here is what you need to do:

1) Create a NumericProperty in the ScoreScreen for the count_r. 2) Bind the count_r NumericProperty in WordComprehension to the NumericProperty in ScoreScreen like so : '''in class WordComprehension ''' self.bind(count_r=self.score_screen.setter('count_r')

Now the trick is that you need to have a reference to the ScoreScreen instance in your WordComprehension class. In my example, I assumed you have a reference to it in self.score_screen. I will leave it up to you how you want to assign that reference.

Now whenever count_r is changed in WordComprehension, the setter function (which is essentially a setattr() ) gets called on the 'count_r' property of ScoreScreen.

Another thing: The line wordcomp = WordComprehension() in get_score() is not referencing the same WordComprehension instance that you want. You are creating a new object of class WordComprehension and referencing it's count_r which will be it's default value.

EDIT1 Not sure if this is what you want but this code will make the score property update whenever the count_r property is changed:

import kivy
from kivy.app import App

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.button import Button

from kivy.properties import ObjectProperty
from kivy.properties import NumericProperty


class AppScreen(FloatLayout):
    app = ObjectProperty(None)

class MainMenu(AppScreen):
    pass

class ScoreScreen(AppScreen):
    count_r = NumericProperty(0)
    score = NumericProperty(0)

    # This is making a new WordComprehension object, and thus will not have
    # the correct value of score. We no longer need this is we are binding properties
    # together.
    #def get_score(self):
    #    wordcomp = WordComprehension()
    #    self.score = wordcomp.count_r


class WordComprehension(AppScreen):
    count_r = NumericProperty(0)
    count_w = NumericProperty(0)

    def do_something(self):
        self.count_r += 1


class InterfaceApp(App):
    def build(self):
        self.screens = {}
        self.screens["wordcomp"] = WordComprehension(app=self)
        self.screens["menu"] = MainMenu(app=self)
        self.screens["score"] = ScoreScreen(app=self)
        self.root = FloatLayout()
        self.goto_screen("menu")

        # Bind the two properties together. Whenever count_r changes in the wordcomp
        # screen, the score property in the score screen will reflect those changes.
        self.screens["wordcomp"].bind(count_r=self.screens["score"].setter('score'))

        return self.root

    def goto_screen(self, screen_name):
        self.root.clear_widgets()
        self.root.add_widget(self.screens[screen_name])


if __name__ == "__main__":
    InterfaceApp().run()

Upvotes: 4

Related Questions