David K
David K

Reputation: 213

Building a simple progress bar or loading animation in Kivy?

I am writing a Kivy UI for cmd line utility I have developed. Everything works fine, but some of the processes can take from a few seconds to a few minutes to process and I would like to provide some indication to the user that the process is running. Ideally, this would be in the form of a spinning wheel or loading bar or something, but even if I could update my display to show the user that a process is running, it would be better than what I have now.

Currently, the user presses a button in the main UI. This brings up a popup that verifies some key information with the user, and if they are happy with those options, they press a 'run' button. I have tried opening a new popup to tell them that the process is running, but because the display doesn't update until the process finishes, this doesn't work.

I have a lot of coding experience, but mostly in the context of math and engineering, so I am very new to the designing of UIs and having to handle events and threads. A simple self-contained example would be greatly appreciated.

Upvotes: 10

Views: 14665

Answers (3)

Jacob Ellis
Jacob Ellis

Reputation: 1

I had this issue but found the discussion here a bit beyond my understanding.

I looked at a LOT of answers but nothing made sense until I researched the threading module a bit more. The Kivy support channel on discord also were incredibly helpful so props to them.

My workaround has been to create a loading screen. I will attach the code here for people who similarly need a simple reproducible code snippet :

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty, ListProperty, StringProperty, 
NumericProperty
import threading
import time
from kivy.clock import mainthread


class WindowManager(ScreenManager):
    pass

class WelcomeWindow(Screen):

    def generateData(self):
        data = []
        for i in range (100):
            #print(i)
            data.append(i)
        time.sleep(3)
        self.set_screen()
        return data

def executeFunc(self):
    self.manager.current = 'Loading' # Here is where I have tried to move to loading screen while func runs
    t1 = threading.Thread(target=self.generateData)# Here is where I have tried to thread the function
    t1.start()
    #t1.join() #Here is where I have tried to wait until func finished before changing screen
    #self.manager.current = 'Final'

@mainthread
def set_screen(self):
    self.manager.current = 'Final'



class LoadingWindow(Screen):
    pass

class FinalWindow(Screen):
    pass

KV = '''
WindowManager:
    WelcomeWindow:
    LoadingWindow:
    FinalWindow:

<WelcomeWindow>:
    name:'Welcome'
    BoxLayout:
        Label:
            text: "JUST SOME TEXT"
        Button:
            text: "Generate Data"
            font_size: sp(30)
            size_hint: .4,.4
            on_release:
                root.executeFunc()
                #app.root.current = "Loading"
                root.manager.transition.direction = "left"

<LoadingWindow>:
    name: 'Loading'
    BoxLayout:
        Label: 
            text: "LOADING SCREEN"
        Button:
            text: "Go Back"
            on_release:
                app.root.current = "Welcome"
                root.manager.transition.direction = "right"
                
<FinalWindow>:
    name: 'Final'
    BoxLayout:
        Label: 
            text: "FINISHED"
'''''

class TestApp(App):
    def build(self):
        return Builder.load_string(KV)

TestApp().run()

Upvotes: 0

Michal Bahn&#237;k
Michal Bahn&#237;k

Reputation: 88

I've dealt with similar problem and creating new thread didn't do the trick. I had to use Clock.schedule_once(new_func) function. It schedules function call to the next frame, so it is going to run almost immediately after callback ends.

Upvotes: 1

Dirty Penguin
Dirty Penguin

Reputation: 4402

I was recently tackling the problem you described: display doesn't update until the process finishes

Here is a complete example that I got working with the help of @andy_s in the #Kivy IRC channel:

My main.py:

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

import time, threading

class PopupBox(Popup):
    pop_up_text = ObjectProperty()
    def update_pop_up_text(self, p_message):
        self.pop_up_text.text = p_message

class ExampleApp(App):
    def show_popup(self):
        self.pop_up = Factory.PopupBox()
        self.pop_up.update_pop_up_text('Running some task...')
        self.pop_up.open()

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

        # Call some method that may take a while to run.
        # I'm using a thread to simulate this
        mythread = threading.Thread(target=self.something_that_takes_5_seconds_to_run)
        mythread.start()

    def something_that_takes_5_seconds_to_run(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 example.kv:

AnchorLayout:
    anchor_x: 'center'
    anchor_y: 'center'
    Button:
        height: 40
        width: 100
        size_hint: (None, None)
        text: 'Click Me'
        on_press: app.process_button_click()

<PopupBox>:
    pop_up_text: _pop_up_text
    size_hint: .5, .5
    auto_dismiss: True
    title: 'Status'   

    BoxLayout:
        orientation: "vertical"
        Label:
            id: _pop_up_text
            text: ''

If you run this example, you can click the Click Me button, which should open up a "progress bar" in the form of a modal/pop-up. This pop up will remain open for 5 seconds without blocking the main window. After 5 seconds, the pop up will automatically be dismissed.

Upvotes: 11

Related Questions