I\'m experimenting with some kivy code. I tried modifing a kivy property(text_colour
) from a tread created with threading lib. Program works fine but the thread
It works for me:
from kivy.app import App
from kivy.uix.label import Label
import threading
import time
class ScatterTextWidget(Label):
def __init__(self,**kwargs):
self.text = 'nima'
self.color = [0, 1, 1, 1]
super(ScatterTextWidget, self).__init__(**kwargs)
a = ScatterTextWidget()
def zaaa():
import time
time.sleep(3)
a.color = [0, 0, 1, 1]
print "function ran"
t = threading.Thread(target= zaaa)
t.start()
class TataApp(App):
def build(self):
return a
if __name__ == "__main__":
TataApp().run()
You cannot modify a kivy property or do any OpenGL related work from an external thread.
The solution is to schedule callbacks with kivy's Clock that will call a function from kivy's main thread and do the work for you.
Personally, when I use a second thread, I use a queue for the inter-thread comm as follows:
from Queue import Queue
class KivyQueue(Queue):
'''
A Multithread safe class that calls a callback whenever an item is added
to the queue. Instead of having to poll or wait, you could wait to get
notified of additions.
>>> def callabck():
... print('Added')
>>> q = KivyQueue(notify_func=callabck)
>>> q.put('test', 55)
Added
>>> q.get()
('test', 55)
:param notify_func: The function to call when adding to the queue
'''
notify_func = None
def __init__(self, notify_func, **kwargs):
Queue.__init__(self, **kwargs)
self.notify_func = notify_func
def put(self, key, val):
'''
Adds a (key, value) tuple to the queue and calls the callback function.
'''
Queue.put(self, (key, val), False)
self.notify_func()
def get(self):
'''
Returns the next items in the queue, if non-empty, otherwise a
:py:attr:`Queue.Empty` exception is raised.
'''
return Queue.get(self, False)
The second threads uses put to put things on the queue. And the callback, when called, schedules a kivy callback using a trigger. The function that the kivy's main thread then calls calls the get function of the queue and sets the appropriate property.
That is helpful if you need to set many properties. If you just need to set a single property, what I do is from the second thread I schedule a callabck that uses partial to make the value part of the function. E.g:
# this may only be called from the main kivy thread
def set_property(value, *largs):
self.kivy_property = value
# the second thread does this when it wants to set self.kivy_property to 10
Clock.schedule_once(partial(set_property, 10))