mdoc-2011
mdoc-2011

Reputation: 2997

Kivy does not play gif in Popup while other code is running

In my Kivy app, I have a function that takes a long time to complete. I made a popup to inform the user the function is running. I want to have a gif animation so that the user knows the app did not crash. In testing, the gif played as expected in the popup, until I add the long running function, then only a stationary image is displayed. Everything else works as expected (e.g. the popup closes at the end of the function).

tl;dr

How can I make a gif continue to play in my kivy app while a function is being executed?

Code Summary

I largely followed the code provide by Dirty Penguin's answer in Building a simple progress bar or loading animation in Kivy?:

from kivy.app import App
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
from kivy.clock import Clock

import time, threading

class RunningPopup(GridLayout):
    fpop = None

    def set_pop(self, pwin):
        self.fpop = pwin

    def close(self):
        self.fpop.dismiss()

class ExampleApp(App):
    def show_popup(self):
        self.pop_up = RunningPopup()
        self.pop_up.open()

    def process_button_click(self):
        # Open the pop up
        self.show_popup()

        # the original code suggested by dirty penguin
        # mythread = threading.Thread(target=self.really_long_function)
        # mythread.start()

        # I've had better luck with the Clock.schedule_once
        Clock.schedule_once(self.really_long_function)

    def really_long_function(self):
        thistime = time.time() 
        while thistime + 5 > time.time(): # 5 seconds
            time.sleep(1)

        # Once the long running task is done, close the pop up.
        self.pop_up.dismiss()

if __name__ == "__main__":
    ExampleApp().run()

My KV file:

<RunningPopup>:
    rows: 3

    Label:
        size_hint_y: 0.2
        text: 'Experiments are running ... '
        bold: True
        color: hex('#0DB14B')

    Label:
        size_hint_y: 0.2
        text: 'Please be patient, this may take time.'
        color: hex('#0DB14B')

    Image:
        size_hint_y: 0.6
        id: loading_animation_gif
        height: dp(200)
        source: './graphics/loading.gif'
        center_x: self.parent.center_x
        center_y: self.parent.center_y
        allow_stretch: True
        size_hint_y: None
        anim_delay: 0.05
        mipmap: True

Tried

Related

Upvotes: 1

Views: 817

Answers (1)

John Anderson
John Anderson

Reputation: 39072

Using the Thread will work. Here is a modified version of your code that uses threading. I had to make a few changes just to get your code to run:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.popup import Popup

import time, threading

kv = '''
#:import hex kivy.utils.get_color_from_hex
Button:
    text: 'doit'
    on_release: app.process_button_click()
    
<RunningPopup>:
    GridLayout:
        rows: 3
    
        Label:
            size_hint_y: 0.2
            text: 'Experiments are running ... '
            bold: True
            color: hex('#0DB14B')
    
        Label:
            size_hint_y: 0.2
            text: 'Please be patient, this may take time.'
            color: hex('#0DB14B')
    
        Image:
            size_hint_y: 0.6
            id: loading_animation_gif
            height: dp(200)
            source: 'elephant.gif'  # my gif file
            center_x: self.parent.center_x
            center_y: self.parent.center_y
            allow_stretch: True
            size_hint_y: None
            anim_delay: 0.05
            mipmap: True
'''

class RunningPopup(Popup):
    fpop = None

    def set_pop(self, pwin):
        self.fpop = pwin

    def close(self):
        self.fpop.dismiss()

class ExampleApp(App):
    def build(self):
        return Builder.load_string(kv)

    def show_popup(self):
        self.pop_up = RunningPopup()
        self.pop_up.open()

    def process_button_click(self):
        # Open the pop up
        self.show_popup()

        # the original code suggested by dirty penguin
        mythread = threading.Thread(target=self.really_long_function)
        mythread.start()

        # I've had better luck with the Clock.schedule_once
        # Clock.schedule_once(self.really_long_function)

    def really_long_function(self, *args):
        thistime = time.time()
        while thistime + 5 > time.time(): # 5 seconds
            time.sleep(1)

        # Once the long running task is done, close the pop up.
        self.pop_up.dismiss()

if __name__ == "__main__":
    ExampleApp().run()

Upvotes: 2

Related Questions