Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I update Kivy elements from a thread?

I have a socket client that calls a View() class every time it receives a message. I've split my code in such a way such that this class can simply use print() or any other display method, as I like. However, it seems that Kivy is not fond of this method. I've extended Kivy's BoxLayout class for my view and can call the message() function. The class looks something like this:

class View(BoxLayout):
    def __init__(self, **kwargs):
        super(View, self).__init__(**kwargs)
        self.btn = Button(text='Default')
        # Bind button press method
        self.btn.bind(on_press=self.message)
        self.add_widget(self.btn)
    def message(self, message):
        self.btn.text = 'Meow'
        self.add_widget(Button(text='Meow'))
        print(str(message))

The message function is indeed called and it prints but the interface does not update. When I press the button however, it does update the interface as well as print.

I've looked into using a StringProperty to modify the button text but have failed that too. Just as a note, in case what I'm doing is completely infeasible, I'm trying to later draw an entire interface consisting of width * height buttons, in the form of a board.

Any input is very much appreciated, it's been driving me insane.


EDIT 1* I've followed on a few comments and tried a few things out. I've added a Clock class and have it schedule an update() method from View. The update method simply changes the text of a few elements. I've noticed it works when I schedule it to, as shown below:
def update(self, *args, **kwargs):
    self.btn.text = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase) for i in range(32))
def message(self, message):
    try:
        print(message)
        self.text = 'sending'
    except Exception as e:
        print(e)

The thread now simply assigns the text property as seen in message(). The periodically triggered update() methods works too, assigning random text. Now the issue however that it cannot set the text. This does not work:

def update(self, *args, **kwargs):
    self.btn.text = self.text

I must be doing wrong elsewhere, any suggestions?


EDIT 2* The error I'm trying to debug is here.
like image 819
kiyui Avatar asked Nov 19 '15 16:11

kiyui


1 Answers

Since you don't post a full working example, I can only guess at what your doing. It seems you have an event (incoming message) on a thread, and you want to display some text when this occurs. You need to 'push' UI updates to the main thread, but you don't need to do periodic updates with Clock, you can just schedule one-time calls with Clock.schedule_once.

from functools import partial

def update(self, text, *a):
    self.btn.text = text

def message(self, message):
    Clock.schedule_once(partial(self.update, message), 0)

As inclement mentioned, you can do this 'push to main thread' automatically with the @mainthread decorator:

@mainthread
def update(self, text):
    self.btn.text = text

def message(self, message):
    update(message)

This way, whenever you call update it will be executed on the main thread.

like image 118
bj0 Avatar answered Oct 20 '22 03:10

bj0