在Kivy中构建一个简单的进度条或加载动画

时间:2015-06-02 12:20:16

标签: python user-interface events kivy

我正在为我开发的cmd行实用程序编写一个Kivy UI。一切正常,但有些过程可能需要几秒钟到几分钟才能完成,我想向用户提供一些过程正在运行的指示。理想情况下,这将是旋转轮或装载杆等形式,但即使我可以更新我的显示器以向用户显示进程正在运行,它也会比现在更好。

目前,用户按下主UI中的按钮。这会弹出一个弹出窗口,用户可以验证一些关键信息,如果他们对这些选项感到满意,他们会按下“运行”按钮。按钮。我尝试打开一个新的弹出窗口告诉他们该进程正在运行,但由于显示在更新完成之前不会更新,因此无法正常工作。

我失去了编码经验,但主要是在数学和工程方面,所以我对UI的设计非常陌生,并且必须处理事件和线程。一个简单的自包含示例将非常感激。

3 个答案:

答案 0 :(得分:7)

我最近解决了您所描述的问题:display doesn't update until the process finishes

这是一个完整的例子,我在#Kivy IRC频道的@andy_s的帮助下工作:

我的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()

我的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: ''

如果您运行此示例,则可以单击Click Me按钮,该按钮应以模式/弹出窗口的形式打开“进度条”。此弹出窗口将保持打开状态5秒钟而不会阻止主窗口。 5秒后,弹出窗口将自动被解除。

答案 1 :(得分:0)

我已经处理过类似的问题,创建新线程并不能解决问题。我必须使用Clock.schedule_once(new_func)函数。它将函数调用安排到下一帧,因此它将在回调结束后立即运行。

答案 2 :(得分:0)

我遇到了这个问题,但是发现这里的讨论超出了我的理解范围。

我看了很多答案,但是直到我对线程模块进行了更多研究之后,才有意义。不和谐的Kivy支持频道也非常有用,因此为他们提供了支持。

我的解决方法是创建一个加载屏幕。对于那些同样需要简单可复制代码段的人,我将在此处附加代码:

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()