如何在 python kivy 中正确组合小部件?

How to compose widgets correctly in python kivy?

我想做这样我可以看到循环执行的进度,但是ProgressBar只在这个循环执行后退出。我该如何解决这个问题?

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.progressbar import ProgressBar

class MyApp(App):
    def build(self):
        self.wid = FloatLayout()
        self.prog_bar = ProgressBar(max=99999, pos = [0, -150])
        self.wid.add_widget(self.prog_bar)
        self.prog_bar.value = 0
        for i in range(0, 99999):
            self.prog_bar.value = i
            print(i/99999)
        
        return self.wid
if __name__ == '__main__':
    MyApp().run()

Kivy 使用您 App 的主线程来更新其小部件。你是 运行 你在主线程上的循环,因此 Kivy 无法更新 ProgressBar 直到该循环完成。解决方法是在另一个线程中执行循环:

class MyApp(App):
    def build(self):
        self.wid = FloatLayout()
        self.prog_bar = ProgressBar(max=99999, pos=[0, -150])
        self.wid.add_widget(self.prog_bar)
        self.prog_bar.value = 0
        threading.Thread(target=self.do_progress).start()

        return self.wid

    def do_progress(self):
        for i in range(0, 99999):
            self.prog_bar.value = i
            print(i / 99999)

这不是关于组合,而是关于多任务处理,问题是,如果你像这样做一个锁定循环,kivy 在循环完成之前不能更新任何东西(因为函数 运行s,没有更新 UI 或处理任何事件)。所以你想要做的是允许循环 运行 “同时”到 kivy 事件循环。

做这种事情有多种方法,它们或多或少都适合不同的情况。

kivy Clock 允许将一个函数调度到 运行 以后(schedule_once),可能多次(schedule_interval),所以你可以有一个更原子的函数,它只是递增进度条的值一次(不在循环中),并使用计划间隔调用此函数。问题是,如果您想在函数中进行实际工作,可能会花费足够多的时间让 UI 在进度条值的增量之间被明显阻塞。

如果是这种情况,因为您尝试 运行 的函数很慢,那么您可能希望 运行 线程中的函数,这样它就不会阻塞 kivy UI。您将能够从线程更新进度条的值,如有必要,使用 Clock 来安排一个函数来执行此操作(您需要注意不要执行从线程更新 OpenGL 上下文的操作,因为 OpenGL 没有指定这种情况下的行为,所以一些图形 cards/drivers 可以正常使用,但其他图形会使你的程序崩溃,所以任何从线程更新 UI 的东西都必须使用时钟,时钟模块中为此提供了一个 mainthread 装饰器。

在某些情况下,你可以(因此想要)避免事先做长时间的工作,找到一种方法每次需要做最少的工作,然后去继续工作,这个想法有一些抽象,比如 RecycleView,当你想要显示很长的项目列表时,它可以避免完全创建小部件,只需预先创建数据,然后让它处理 creation/update 根据需要添加小部件。当然,这可能不适用于您的情况,但值得一提的是,以防万一。

所以简而言之:不要做这样的循环,或者在线程中做,如果你真的需要这样的循环,请认真思考,并在使用线程时使用时钟事件来更新你的UI。