Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kivy: Label text does not update during for-loop

I have an issue when I try to update a label text during a for-loop. There are similar entries (e.g.: Update properties of a kivy widget while running code) but they do not seem to fit my issue exactly (or I missed the point…). I run following code:

*.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
#from time import sleep

class MyBox(BoxLayout):
    tobeupd = StringProperty()

    def __init__(self,*args,**kwargs):
        super(MyBox,self).__init__(*args,**kwargs)
        self.tobeupd = '#'

    def upd_ltxt(self):
        for i in range(1,10):
            self.tobeupd = str(i)
            print(self.tobeupd)
            input('Write something: ')  # new line, see edit below
            #sleep(0.5) 

class updApp(App):
    def build(self):
        return MyBox()

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

*.kv

<MyBox>:
    orientation: 'horizontal'
    cols: 2
    Label:
        text: root.tobeupd
    Button:
        text: 'Start Update'
        on_release: root.upd_ltxt()

Whereas the ‘print’ statement updates the shell regularly, the label text updates at the end of the for-loop only. Can anyone explain to me why Kivy works this way and how I can overcome this problem?

EDIT: According to PM2Ring and Gugas, I changed the code in order to avoid the sleep-function. The problem remains if I ask the user to enter something before the loop can be continued. The values are updated in the shell but not on the label.

like image 561
dade100 Avatar asked Mar 01 '17 09:03

dade100


1 Answers

You can use threading for this.
When you do a loop or wait for an input in kivy, the main thread is waiting, and nothing will update on the app. threading will prevent that.
Use threading to make another thread besides the main thread.
Example:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.lang import Builder
import threading

Builder.load_string('''

<MyBox>:
    orientation: 'horizontal'
    cols: 2
    Label:
        text: root.tobeupd
    Button:
        text: 'Start Update'
        on_release: root.upd_ltxt()

''')

class MyBox(BoxLayout):
    tobeupd = StringProperty()

    def __init__(self,*args,**kwargs):
        super(MyBox,self).__init__(*args,**kwargs)
        self.tobeupd = '#'

    def upd_ltxt(self):
        threading.Thread(target=self.update_label).start()

    def update_label(self):
        for i in range(1,10):
            print(self.tobeupd)
            self.tobeupd = str(i)
            input('Write something: ')  # new line, see edit below



class updApp(App):
    def build(self):
        return MyBox()

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

Now its worth mentioning that you can keep pushing the button and start threads, even if the first did not finish yet. This might be an unwanted behavior.
This can be prevented by disabling the button in the beginning of the thread, and enabling it again at the end.

Give the button an id in kv:

Button:
    id: updatebutton
    text: 'Start Update'
    on_release: root.upd_ltxt()

And in the thread do like this:

def update_label(self):

    self.ids.updatebutton.disabled = True

    for i in range(1,10):
        self.tobeupd = str(i)
        input('Write something: ')

    self.ids.updatebutton.disabled = False
like image 69
el3ien Avatar answered Oct 25 '22 23:10

el3ien