简体   繁体   中英

Kivy strange behavior when it updates Image Texture

I am trying to update the Kivy Image.texture with the OpenCV image, and I am using a new thread to do it. I found some discussion that "the graphics operation should be in the main thread". But still, I want to figure out why the code below works.

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
import cv2
from threading import Thread
class MainApp(App):
    def __init__(self):
        super().__init__()
        self.layout = FloatLayout()
        self.image = Image()
        self.layout.add_widget(self.image)
        Thread(target=self.calculate).start()
    def build(self):
        return self.layout
    def calculate(self):
        img = cv2.imread("test.png")
        w, h = img.shape[1], img.shape[0]
        img = cv2.flip(img, flipCode=0)
        buf = img.tostring()
        texture = Texture(0, 0, 0).create(size=(w, h), colorfmt="bgr")
        texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
        self.image.texture = texture
def main():
    Image(source='test.png')  # remove this line will froze the app
    MainApp().run()
if __name__ == "__main__":
    main()

If I remove this line:

Image(source='test.png')

the app is frozen. Can someone help me to understand why decalring an Image object outside the main loop will affect the MainApp running.

The "test.png" image can be any simple image, like below. You need to put the image in the same directory as this script. 在此处输入图片说明

Not sure exactly why that line affects your code, but the main problem is that not only is the GUI manipulation required to be done on the main thread, but also any blit_buffer() must also be done on the main thread. So, a working version of your code (with the Image() removed) looks like this:

from functools import partial

from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
import cv2
from threading import Thread


class MainApp(App):
    def __init__(self):
        super().__init__()
        self.layout = FloatLayout()
        self.image = Image()
        self.layout.add_widget(self.image)
        Thread(target=self.calculate).start()

    def build(self):
        return self.layout

    def calculate(self):
        img = cv2.imread("test.png")
        w, h = img.shape[1], img.shape[0]
        img = cv2.flip(img, flipCode=0)
        buf = img.tostring()
        Clock.schedule_once(partial(self.do_blit, buf, w, h))

    def do_blit(self, buf, w, h, dt):
        texture = Texture(0, 0, 0).create(size=(w, h), colorfmt="bgr")
        texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
        self.image.texture = texture

def main():
    # Image(source='test.png')  # remove this line will froze the app
    MainApp().run()

if __name__ == "__main__":
    main()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM