1

I am programming a multi-subsystem project that involves 3 Raspberry Pis chatting to each other over the local network using sockets. On one of the Pis, I am programming a Kivy app.I am trying to get a label in Kivy to change its text when one of the other Pis has verified that it is OK via a socket.

Definition of Label in .kv file:

Label:
                            id: distance_label
                            markup:True
                            text:"[size=40]Distance:[/size]\n\n[size=60][color=ff0000]NO[/color][/size]"
                            halign:"center"
                            valign:"top"
                            color:black

And then there is an entirely separate Python thread that I am manipulating the sockets with. This pings the subsystem, and then tries to change the label on successful reply:

def test_socket(self, connection):
            Communication.server.send_data(connection,"VERIFY?")
            data_back = Communication.server.receive_data(connection)
            print data_back
            if data_back == "DISTANCE!":
                    # set distance to OK
                    print "Distance is OK"
                    InitScreen().distance_on()

This then in turn tries to trigger the distance_on() method of the InitScreen class. The method appears to be triggered (and it will print out a small debug message if you put it in there), but annoyingly the label text does not change from its original value.

class InitScreen(Screen):
    ........

    def distance_on(self, *args):
            distance_label = self.ids['distance_label']
            distance_label.text = "[size=40]Distance:[/size]\n\n[size=60][color=008000]OK[/color][/size]"
            print distance_label.text

In another part of the code I use the on_event function in Kivy to change the value of a label using a slider. This works perfectly. I have tried to make my code look as similar as possible to this working instance, the only difference being that I am not trying to trigger from a Kivy event, just within the Python.

Can anyone shed some light? Is it something to do with calling another class' method? An ID issue?


This is not a duplicate question. The suggested duplication involves triggering of functions from Kivy events. I would like to trigger a Kivy label change from Python itself - not a Kivy event like touching a button for example.


Here is all the code in question. I think this issue is conflated by the fact I am using a ScreenManager, but I can't not do that!

PROGRAM:

class Communication(threading.Thread):
    server = serv.Server()

    def run(self):
            self.setup()
            while distance == False:
                    (connection, address) = self.awaiting_socket()
                    self.test_socket(connection)

    def setup(self):
            Communication.server.setup_server()
            print "SUCCESS ON BIND"

    def awaiting_socket(self):
            print "AWAITING"
            (connection, address) = Communication.server.socket_reception()
            return (connection, address)

    def test_socket(self, connection):
            Communication.server.send_data(connection,"VERIFY?")
            data_back = Communication.server.receive_data(connection)
            print data_back
            if data_back == "DISTANCE!":
                    # set distance to OK
                    print "Distance is OK"
                    application.InitScreen.distance_on()
                    #global distance
                    #distance = True
            if data_back == "STEPPER!":
                    # set stepper to OK
                    print "Stepper is OK"
            ..............

class InitScreen(Screen):
    def power_off(self, *args):
            onoffswitch = self.ids["onoffswitch"]
            onoff_value = onoffswitch.active
            if onoff_value == False:
                    subprocess.call(powerdown)

    def distance_on(self):
            distance_label = self.ids["distance_label"]
            distance_label.text = "[size=40]Distance:[/size]\n\n[size=60][color=008000]OK[/color][/size]"

class ScreenManagement(ScreenManager):
    pass

application = Builder.load_file("main.kv")

class LidarApp(App):
           def build(self):
                return application

KIVY DEFINING SCREEN MANAGER:

ScreenManagement:
    transition: FadeTransition()
    InitScreen:
    MainScreen:

KIVY DEFINING LABEL IN QUESTION:

Label:
                            id: distance_label
                            markup:True
                            text:"[size=40]Distance:[/size]\n\n[size=60][color=ff0000]NO[/color][/size]"
                            on_text:
                            halign:"center"
                            valign:"top"
                            color:black
BigCatUK
  • 13
  • 3
  • Possible duplicate of [How to change text of a label in the kivy language with python](http://stackoverflow.com/questions/26656164/how-to-change-text-of-a-label-in-the-kivy-language-with-python) – Amin Etesamian Feb 10 '17 at 19:32
  • set label a StringProperty() and then set the text of the label as this varibale. it will be updated then .`label = StringProperty() \n distance_variable.text = label` – Amin Etesamian Feb 10 '17 at 19:34
  • This thread isn't quite a duplicate of the aforementioned. I am quite unfamiliar with StringProperty()s? Would you mind explaining how that would be used in my situation? – BigCatUK Feb 10 '17 at 19:47
  • ` InitScreen().distance_on()` creates a *new* instance of InitScreen, but never does anything with it, whereas it sounds like you have an existing InitScreen whose behaviour you want to change. You need to get a reference to this existing InitScreen and call the `distance_on` method of that instance. – inclement Feb 10 '17 at 19:56
  • Ah of course. I knew it was a Python error on my part. Still fairly new to classes, threading etc. How would you suggest getting that reference to the existing InitScreen? – BigCatUK Feb 10 '17 at 19:59
  • Really not sure how to reference the existing InitScreen as I am using a ScreenManager. Trying something like this just gives me an unbound method saying that it must be called with InitScreen instance as first argument(got ScreenManagement instance instead): `if data_back == "DISTANCE!": # set distance to OK print "Distance is OK" InitScreen.distance_on(application) ` – BigCatUK Feb 10 '17 at 20:41
  • Have you tried `InitScreen.distance_on()` ? Also from what i understand from your code, you are not changing the label, but instead you are just adding it when "Distance is OK". If this is the case, couldn't you instead of creating the label in the function, just create the `distance_label` in your InitScreen class and when your case is true, just 'self.add_widget.distance_label' – George Bou Feb 10 '17 at 22:16
  • In the previous comment i meant `self.add_widget(distance_label)` – George Bou Feb 10 '17 at 22:24
  • When I just use `InitScreen.distance_on()` I get an unbound method error: `TypeError: unbound method distance_on() must be called with InitScreen instance as first argument (got nothing instead)` Sadly that is not the case. The label already exists and I just want to change its text from "NO" to "OK" – BigCatUK Feb 11 '17 at 09:53
  • Added more of the program to help people to understand! Thanks for the help so far! – BigCatUK Feb 11 '17 at 10:03

1 Answers1

0

As discussed in the comments in test_socket, you need to get the current screen. To do this, you need to use ScreenManager.current_screen.

Builder.load_file returns a ScreenManager. This means that you need to use your application global variable.

Your completed code:

def test_socket(self, connection):
    Communication.server.send_data(connection,"VERIFY?")
    data_back = Communication.server.receive_data(connection)
    print data_back
    if data_back == "DISTANCE!":
            # set distance to OK
            print "Distance is OK"
            application.current_screen.distance_on()
muddyfish
  • 3,530
  • 30
  • 37