Search code examples
pythondynamickivylabelwindow

How to dynamically update a Kivy label from a continuously running function?


I am trying to have a function run continuously and spit out a distance that the label uses that I'll eventually tie to a sonar module, but the label remains blank and I am at a loss as to what I am doing wrong. If I just add a print statement for that distance variable it prints and updates just fine, just can't get the label to use it.

Part II of my question is how do I reference my same function in the second window and also have a label that updates from that same function?

Thanks for the help in advance, I am very very new to kivy and just started learning python a few months ago as well.

Python code:

from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen  # for multiple screens
from kivy.properties import StringProperty


class MySonar(Screen):
    global i
    i = 1

    distance = StringProperty("")

    #Generic function that just adds itself up, just using to try and get the label to change before I throw in my real function
    def sonar(self):
        global i

        if i < 250:
            distance = (10 + .1 * i)
            i += 1

        else:
            i = 1
            distance = 10

        self.root.distance=str(distance)

class DropWindow(Screen):
    pass

class WindowManager(ScreenManager):
    pass

kv = Builder.load_file("help.kv")

class HelpMe(App):
    def build(self):

        #running interval update to keep running code above
        Clock.schedule_interval(lambda dt: MySonar.sonar(self), 0.1)

        return kv

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

Kivy:

WindowManager:
    MySonar:
    DropWindow:

<MySonar>:
    name:"Main"

    GridLayout:
        cols:1

        ##Need this to update
        Label:
            text:root.distance
        Button:
            text:"Next Window"
            on_release:
                app.root.current="Drop"
                root.manager.transition.direction="left"


<DropWindow>:
    name:"Drop"

    GridLayout:

        cols:1

        ##Need this to update, commented out the text so the program will run and you can see the blank label for part one of my question
        Label:
            ##text:root.distance

        Button:
            text:"Cancel"
            on_release:
                app.root.current="Main"
                root.manager.transition.direction="right"

Solution

  • To simplify the access to distance, you can put that StringProperty in the HelpMe App:

    class MySonar(Screen):
        global i
        i = 1
    
        #Generic function that just adds itself up, just using to try and get the label to change before I throw in my real function
        def sonar(self):
            global i
    
            if i < 250:
                distance = (10 + .1 * i)
                i += 1
    
            else:
                i = 1
                distance = 10
    
            # set the value of distance in the StringProperty of the App
            App.get_running_app().distance=str(distance)
            print(distance)
    
    class DropWindow(Screen):
        pass
    
    class WindowManager(ScreenManager):
        pass
    
    # kv = Builder.load_file("help.kv")
    
    
    class HelpMe(App):
        distance = StringProperty('')
    
        def build(self):
            kv = Builder.load_file("help.kv")
    
            #running interval update to keep running code above
            sonar_instance = kv.get_screen('Main')
            Clock.schedule_interval(lambda dt: sonar_instance.sonar(), 0.1)
    
            return kv
    
    if __name__ == "__main__":
        HelpMe().run()
    

    Note that I have also moved the Builder.load_file() inside the App. This is good practice when you reference app in the kv file (as I have done). Also, calling the sonar() method using MySonar.sonar(self) will not work. You need to use a reference to the instance of MySonar that is in your GUI.

    Now the kv file becomes:

    WindowManager:
        MySonar:
        DropWindow:
    
    <MySonar>:
        name:"Main"
    
        GridLayout:
            cols:1
    
            ##Need this to update
            Label:
                text: app.distance
            Button:
                text:"Next Window"
                on_release:
                    app.root.current="Drop"
                    root.manager.transition.direction="left"
    
    
    <DropWindow>:
        name:"Drop"
    
        GridLayout:
    
            cols:1
    
            ##Need this to update, commented out the text so the program will run and you can see the blank label for part one of my question
            Label:
                text: app.distance
    
            Button:
                text:"Cancel"
                on_release:
                    app.root.current="Main"
                    root.manager.transition.direction="right"
    

    The change is that the text attribute of both Labels is now just app.distance.