Alterar una propiedad kivy de otro hilo.

Estoy experimentando con un código kivy. Intenté modificar una propiedad kivy ( text_colour ) de una banda de rodadura creada con lib de subprocesos. El progtwig funciona bien pero el hilo no cambia la propiedad.

También intenté crear un método en clase sobre el cual se obtiene el valor como un argumento, que también falló.

 from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.properties import ListProperty import threading import random import time def zaaa(): import time time.sleep(3) ScatterTextWidget.text_colour = [0, 0, 1, 1] print "function ran" t = threading.Thread(target= zaaa) t.start() class ScatterTextWidget(BoxLayout): text_colour = ListProperty([1, 0, 0, 1]) def change_label_colour(self, *args): colour = [random.random() for i in xrange(3)] + [1] self.text_colour = colour def press(self, *args): self.text_colour = [1, 1, 1, 1] class TataApp(App): def build(self): return ScatterTextWidget() if __name__ == "__main__": TataApp().run() 

SALIDA:

 [INFO ] Kivy v1.8.0 [INFO ] [Logger ] Record log in /home/mbp/.kivy/logs/kivy_14-02-26_44.txt [INFO ] [Factory ] 157 symbols loaded [DEBUG ] [Cache ] register  with limit=None, timeout=Nones [DEBUG ] [Cache ] register  with limit=None, timeout=60s [DEBUG ] [Cache ] register  with limit=None, timeout=Nones [INFO ] [Image ] Providers: img_tex, img_dds, img_pygame, img_pil, img_gif [DEBUG ] [Cache ] register  with limit=1000, timeout=60s [DEBUG ] [Cache ] register  with limit=1000, timeout=3600s [DEBUG ] [App ] Loading kv  [DEBUG ] [Window ] Ignored  (import error) [INFO ] [Window ] Provider: pygame(['window_egl_rpi'] ignored) libpng warning: iCCP: known incorrect sRGB profile [DEBUG ] [Window ] Display driver x11 [DEBUG ] [Window ] Actual window size: 800x600 [DEBUG ] [Window ] Actual color bits r8 g8 b8 a8 [DEBUG ] [Window ] Actual depth bits: 24 [DEBUG ] [Window ] Actual stencil bits: 8 [DEBUG ] [Window ] Actual multisampling samples: 2 [INFO ] [GL ] OpenGL version  [INFO ] [GL ] OpenGL vendor  [INFO ] [GL ] OpenGL renderer  [INFO ] [GL ] OpenGL parsed version: 4, 3 [INFO ] [GL ] Shading version  [INFO ] [GL ] Texture max size  [INFO ] [GL ] Texture max units  [DEBUG ] [Shader ] Fragment compiled successfully [DEBUG ] [Shader ] Vertex compiled successfully [DEBUG ] [ImagePygame ] Load  [INFO ] [Window ] virtual keyboard not allowed, single mode, not docked [INFO ] [Text ] Provider: pygame [DEBUG ] [Cache ] register  with limit=500, timeout=60s [INFO ] [Loader ] using a thread pool of 2 workers [DEBUG ] [Cache ] register  with limit=None, timeout=60.0s [DEBUG ] [Cache ] register  with limit=None, timeout=60.0s [DEBUG ] [Atlas ] Load  [DEBUG ] [Atlas ] Need to load 1 images [DEBUG ] [Atlas ] Load  [DEBUG ] [ImagePygame ] Load  [INFO ] [GL ] NPOT texture support is available [INFO ] [OSC ] using  for socket [DEBUG ] [Base ] Create provider from mouse [DEBUG ] [Base ] Create provider from probesysfs [DEBUG ] [ProbeSysfs ] using probsysfs! [INFO ] [Base ] Start application main loop  function ran [INFO ] [Base ] Leaving application in progress... 

No puede modificar una propiedad kivy o hacer ningún trabajo relacionado con OpenGL desde un hilo externo.

La solución es progtwigr las devoluciones de llamada con el Reloj de Kivy que llamará a una función desde el hilo principal de Kivy y hará el trabajo por usted.

Personalmente, cuando uso un segundo hilo, uso una cola para la comunicación entre subprocesos de la siguiente manera:

 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) 

Los segundos hilos utilizan poner para poner las cosas en la cola. Y la callback, cuando se le llama, progtwig una callback kivy utilizando un disparador . La función que el subproceso principal de kivy llama a la función obtener de la cola y establece la propiedad apropiada.

Eso es útil si necesita establecer muchas propiedades. Si solo necesita establecer una sola propiedad, lo que hago es desde el segundo hilo que programo un callabck que usa parcial para hacer que el valor sea parte de la función. P.ej:

 # 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)) 

Esto funciona para mi:

 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()