Search code examples
pythonimagekivyrenderingkivymd

Image not showing when created from a thread kivy/kivymd


I'm working on an app, and I need the images to display independently at a specific timing. I have set up a thread using python's stock threading module, it runs and works normally instead of the image it displays a black square. Does anyone know how to fix it?

Here is my code to reproduce the issue:

import threading
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout

class TestApp(App):
    def build(self):
        self.fl = FloatLayout()
        self.fl.add_widget(Button(text="show image", on_press=self.start_thread))
        return self.fl

    def insertfunc(self):
        self.fl.add_widget(Image(source="HeartIcon.png"))

    def start_thread(self, instance):
        threading.Thread(target=self.insertfunc).start()

TestApp().run()

Any help will be appreciated!


Solution

  • The add_widget() must be done on the main thread. I assume that you are using threading because you have additional things to do on the Thread aside from just the add_widget(). Based on that assumption, here is a modified version of your code that does what I think you want:

    import threading
    
    from kivy.app import App
    from kivy.clock import Clock
    from kivy.uix.image import Image
    from kivy.uix.button import Button
    from kivy.uix.floatlayout import FloatLayout
    
    class TestApp(App):
        def build(self):
            self.fl = FloatLayout()
            self.fl.add_widget(Button(text="show image", on_press=self.start_thread))
            return self.fl
    
        def insert_image(self, dt):
            self.fl.add_widget(Image(source="HeartIcon.png"))
    
        def insertfunc(self):
            # do some calculations here
            Clock.schedule_once(self.insert_image)
    
        def start_thread(self, instance):
            threading.Thread(target=self.insertfunc).start()
    
    TestApp().run()
    

    If you are not doing anything else in the new thread, then you don't actually need another thread. The start_thread() method can just do the:

    self.fl.add_widget(Image(source="HeartIcon.png"))