Reputation: 77
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
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