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))
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…