oddity
oddity

Reputation: 77

Threading Problem Kivy image texture not updating when threading is used

I am trying to make a application using Kivy that use threading. The background thread updates the texture in the Image Widget main class.

The problem is that the texture variable gets updated but is not displayed in the kivy Gui app

The here's the code I have used ObjectProperty to trigger update both label:text and the Image:Texture But only the Label:text gets really updated

import random
import threading

from kivy.app import App

from kivy.core.image import Texture

from kivy.lang import Builder
from kivy.properties import NumericProperty, ObjectProperty
from kivy.uix.widget import Widget

Builder.load_string('''
<Main>:

    btn1:btn1
    label1:label1
    img1:img1
    GridLayout:
        cols:1
        size:root.width,root.height
        Button:
            text:"Hello"
            id:btn1
            on_release:root.btn()

        Label:
            id:label1
            text:"hello"
        Image:
            id:img1

''')


class Main(Widget):
    btn1 = ObjectProperty(None)
    label1 = ObjectProperty(None)
    img1 = ObjectProperty(None)
    a = ObjectProperty(1)
    newtexture = ObjectProperty(2)



    update = False
    iter = 1

    def btn(self):
        self.update = not self.update
        t1 = threading.Thread(target=self.updateValue)
        t1.start()
        # self.updateValue()

    def updateValue(self):
        while (self.update):
            self.a += 2

            testexture = Texture.create(size=(512, 512), colorfmt='rgb')
            size = 512 * 512 * 3

            if self.iter == 1:
                buf = [int(x % 128) for x in range(size)]
                self.iter = 0
                # print("strip")
            else:
                buf = [int(x % 256) for x in range(size)]
                self.iter = 1
                # print("random")

            buf = bytearray(buf)
            testexture.blit_buffer(buf, colorfmt='rgb', bufferfmt='ubyte')
            self.newtexture = testexture
            # print("Generated")

    def on_a(self, instance, value):
        print('My property a changed to', value)
        self.label1.text = str(value)

    def on_newtexture(self, instance, value):
        self.img1.texture = value
        self.img1.canvas.ask_update()
        print("updated texture")
        print(value)

class MaainApp(App):
    def build(self):
        return Main()


MaainApp().run()

Another thing if you Remove the While loop in update And Threading , and just trigger trigger with button it works perfectly fine . But that is of no use to me for my App

please can any one tell whats actually happening and how can I update the image texture using thread.

Thanks

Upvotes: 2

Views: 1415

Answers (1)

John Anderson
John Anderson

Reputation: 38962

I've been bitten by what I think is the same problem that you are encountering. As I am sure you know, changes to the GUI must be made on the main thread. Your on_a() and on_newtexture() will always be run on the main thread, so it would seem that everything is fine in that area. However, the Texture class handles OpenGL textures and I believe that forces the same constraint on using the blit_buffer() method of Texture. So here is a modified version of your Main Widget that works for me, using Clock.schedule_once() to get the call to blit_buffer() back on the main thread:

class Main(Widget):
    btn1 = ObjectProperty(None)
    label1 = ObjectProperty(None)
    img1 = ObjectProperty(None)
    a = ObjectProperty(1)
    newtexture = ObjectProperty(2)

    update = False
    iter = 1

    def btn(self):
        self.update = not self.update
        t1 = threading.Thread(target=self.updateValue, daemon=True)
        t1.start()
        # self.updateValue()

    def updateValue(self):
        while (self.update):
            self.a += 2

            testexture = Texture.create(size=(512, 512), colorfmt='rgb')
            size = 512 * 512 * 3

            if self.iter == 1:
                buf = [int(x % 128) for x in range(size)]
                self.iter = 0
                # print("strip")
            else:
                buf = [int(x % 256) for x in range(size)]
                self.iter = 1
                # print("random")

            buf = bytearray(buf)
            Clock.schedule_once(partial(self.updateTexture, testexture, buf))
            # print("Generated")

    def updateTexture(self, testexture, buf, *args):
        testexture.blit_buffer(buf, colorfmt='rgb', bufferfmt='ubyte')
        self.newtexture = testexture

    def on_a(self, instance, value):
        print('My property a changed to', value)
        self.label1.text = str(value)

    def on_newtexture(self, instance, value):
        self.img1.texture = value
        self.img1.canvas.ask_update()
        print("updated texture")
        print(value)

Upvotes: 2

Related Questions