Search code examples
pythonkivykivy-languageupdating

Updating Labels across Screens in Kivy (Python) - Problem with (lack of) interactions between .kv and .py files with ScreenManager


I'm trying to build a Kivy application that has 2 screens which are re-used over and over again with different text.

So I go from a FirstScreen with a Label that says "First1" to a SecondScreen with a Label that says "Second1", and then back to the FirstScreen but this time with the Label "First2", then SecondScreen and "Second2", and so on and so forth.

The code for this is pretty straightforward, but there seems to be a problem in updating the Label text without a designated update button. For some reason, my Python code manages to update the text, but it isn't updated in my .kv file. So for instance, my print statements will tell me that the Label text is "First2", but Kivy displays "First1" for me. I've illustrated this in the Screenshot below:

enter image description here

By adding a Button that updates the text on press, everything is updated, synced up and works, but I'd really like it to work without the extra user input. Does anybody know how I can go about this? I've scoured the docs and stackoverflow questions left and right but can't seem to find the answer to my seemingly simple problem.

Here's the code:

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty, ObjectProperty
from kivy.lang import Builder

S_ID = 1  # global screen ID. I'm using this to detect what text to use.


class FirstScreen(Screen):
    text = StringProperty("")
    lbl = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(FirstScreen, self).__init__(**kwargs)
        global S_ID
        print("\nS_ID is ", S_ID)
        self.update()

    def update(self):
        print("FIRST - UPDATE")
        if S_ID == 1:
            print("FIRST1")
            self.text = "FIRST1"
        elif S_ID == 2:
            print("FIRST2")
            self.text = "FIRST2"
            print("self.lbl.text", self.lbl.text)
        else:
            print("FIRST ELSE")
            self.text = "FIRST ELSE"

    def pressed(self):
        sm.current = "second"


class SecondScreen(Screen):
    text = StringProperty("")

    def __init__(self, **kwargs):
        super(SecondScreen, self).__init__(**kwargs)
        self.update()

    def update(self):
        print("SECOND - UPDATE")
        if S_ID == 1:
            print("SECOND1")
            self.text = "SECOND1"
        elif S_ID == 2:
            print("SECOND2")
            self.text = "SECOND2"
        else:
            print("SECOND ELSE")
            self.text = "SECOND ELSE"

    def pressed(self):
        global S_ID
        S_ID += 1
        FirstScreen.update(FirstScreen())
        sm.current = "first"


sm = ScreenManager()
kv = Builder.load_file("test.kv")
sm.add_widget(FirstScreen(name='first'))
sm.add_widget(SecondScreen(name='second'))

sm.current = "first"


class MyApp(App):

    def build(self):
        return sm


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

and here's the .kv file:

<FirstScreen>:
    name: "first"
    lbl: lbl:

    GridLayout:
        cols:2
        Label:
            id: lbl
            text: root.text

        Button:
            text: "next"
            on_press: root.pressed()

<SecondScreen>:
    name: "second"

    GridLayout:
        cols:2
        Label:
            text: root.text

        Button:
            text: "next"
            on_press:
                root.pressed()

Solution

  • The problem is your statement:

    FirstScreen.update(FirstScreen())
    

    This statement is creating a new instance of FirstScreen and updating that instance. Unfortunately, that instance is not the one shown in your GUI. You can correct that by replacing the above statement with:

        first_screen = self.manager.get_screen('first')
        first_screen.update()
    

    This code gets the instance of FirstScreen from the ScreenManager and calls update() on that instance.