Altering a kivy property from another thread

后端 未结 2 2058
面向向阳花
面向向阳花 2020-12-10 16:08

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

相关标签:
2条回答
  • 2020-12-10 16:44

    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()
    
    0 讨论(0)
  • 2020-12-10 16:53

    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))
    
    0 讨论(0)
提交回复
热议问题