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

Posted

技术标签:

【中文标题】在 Kivy 中构建简单的进度条或加载动画?【英文标题】:Building a simple progress bar or loading animation in Kivy? 【发布时间】:2015-08-16 05:38:59 【问题描述】:

我正在为我开发的命令行实用程序编写 Kivy UI。一切正常,但有些进程可能需要几秒钟到几分钟的时间来处理,我想向用户提供一些指示,表明该进程正在运行。理想情况下,这将是一个旋转轮或加载栏或其他东西的形式,但即使我可以更新我的显示以向用户显示一个进程正在运行,它也会比我现在拥有的更好。

目前,用户在主 UI 中按下按钮。这会弹出一个弹出窗口,向用户验证一些关键信息,如果他们对这些选项感到满意,他们会按下“运行”按钮。我已经尝试打开一个新的弹出窗口来告诉他们进程正在运行,但是因为在进程完成之前显示不会更新,所以这不起作用。

我有很多编码经验,但主要是在数学和工程方面,所以我对 UI 设计和处理事件和线程非常陌生。一个简单的独立示例将不胜感激。

【问题讨论】:

见this example。也许您可以在自己的应用中使用它。 【参考方案1】:

我最近正在解决您描述的问题: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()

我的例子.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秒后,弹窗会自动消失。

【讨论】:

感谢@Dirty Penguin 的建议。我一直在玩这个,从线程开始,有一个非常奇怪的问题。当我点击我的应用内运行按钮时,我没有调用我的函数,而是调用了一个启动器,它生成一个“正在运行”的弹出窗口并使用我的函数启动一个线程。问题是当作为线程运行时,该函数会做一些奇怪的事情。基本上,我的函数会生成一个基于 csv 的按钮表,当作为线程运行时,其中一些按钮是白色的且不可点击。每次都是不同的按钮,如果我不将它作为单独的线程运行,它就会消失。想法? 原来这些奇怪的按钮是可点击的,它们看起来与我网格中的其他按钮莫名其妙地不同。通常是 1 或 4 个按钮(大约 60 个按钮)来执行此操作。所有其他按钮都有默认的填充颜色,但是这些奇怪的按钮有白色填充,并且在单击时不会突出显示。我能够告诉它们是可点击的唯一方法是将打印绑定到它们。 您应该只在主线程中更改小部件。 github.com/kivy/kivy/issues/6244【参考方案2】:

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

【讨论】:

【参考方案3】:

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

我查看了很多答案,但在我对线程模块进行更多研究之前,没有任何意义。 Discord 上的 Kivy 支持频道也非常有帮助,因此对他们很有帮助。

我的解决方法是创建一个加载屏幕。我将在此处附上代码,供同样需要简单可重现代码 sn-p 的人使用:

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

【讨论】:

以上是关于在 Kivy 中构建简单的进度条或加载动画?的主要内容,如果未能解决你的问题,请参考以下文章

Kivy:为长功能加载动画(避免冻结)

加快加载动画 gif kivy

Unity3D序列帧动画制作方法---实现加载进度条

加载 webview 时加载动画卡住/冻结

加载动图的实现及属性动画的使用

前端页面加载进度条的制作