Kivy:在添加大量小部件时可以使UI响应吗?

4 投票
2 回答
2573 浏览
提问于 2025-04-18 04:14

我有一个应用程序,需要动态添加很多小部件。这里有一个可以模拟这个功能的工作示例:

from threading import Thread

from kivy.app import App
from kivy.uix.stacklayout import StackLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.clock import Clock

class LotsOfWidgets(App):
    def build(self):
        self.widgets_amt = 5000

        root = GridLayout(cols=1)
        self.label_container = StackLayout()

        generate_button = Button(text='Generate lots of labels', 
                              size_hint_y=None, height=44)

        generate_button.bind(on_press=self.generate_lots_of_labels)

        hooray_button = Button(text='Print hooray', 
                               size_hint_y=None, height=44)

        hooray_button.bind(on_press=self.print_hooray)

        for widget in (generate_button, hooray_button, 
                       self.label_container):
            root.add_widget(widget)

        return root

    def generate_lots_of_labels(self, *args):
        for _ in xrange(self.widgets_amt):
            label = Label(text='a', size_hint=(None, None), size=(10,10))
            self.label_container.add_widget(label)

    def scheduled_generate_lots_of_labels(self, *args):
        Clock.schedule_once(self.generate_lots_of_labels)

    def threaded_generate_lots_of_labels(self, *args):
        thread = Thread(target=self.generate_lots_of_labels)
        thread.start()

    def print_hooray(self, *args):
        print 'hooray'

LotsOfWidgets().run()

我们有一个网格布局,里面有两个按钮和一个堆叠布局。点击第一个按钮后,会在堆叠布局中生成5000个标签。第二个按钮只是向控制台打印“hooray”。

将5000个小部件添加到堆叠布局并在屏幕上绘制它们需要一些时间,这没问题。当你按下生成标签的按钮后,立刻按下“打印hooray”的按钮时,在我的电脑上,hooray大约在标签出现在屏幕上3秒后才会打印出来。所以问题是,在生成标签的时候,用户界面变得没有响应。

我尝试通过线程来解决这个问题,把generate_button.on_press绑定到scheduled_generate_lots_of_labelsthreaded_generate_lots_of_labels(当然不是同时)的方法上,而不是代码中显示的方法,但似乎没有什么帮助。

有没有什么办法可以让用户界面在生成这些小部件时仍然保持响应?

2 个回答

0

我参考了brousch在他回答中提到的想法。首先我尝试把那5000个项目分成小块,然后在每个小块里用自己的Clock.schedule_once来逐个处理。

结果发现,这样做的效果和一次性处理5000个项目差不多。如果你把它安排在1秒后执行,那你就有1秒的时间去点击“好耶”按钮。之后,界面就会变得无响应,直到所有的组件都生成完毕。

所以在大多数情况下,唯一的选择就是使用Clock.schedule_interval,这里有个实验的例子。build方法保持不变。

def chunk(self, l, n):
    for i in xrange(0, len(l), n):
        yield l[i:i+n]

def generate_lots_of_labels(self, *args):
    n = 500
    interval = 0.01

    self.chunks = list(self.chunk(range(self.widgets_amt), n))
    self.i = 0

    Clock.schedule_interval(self.iterate, interval)

def iterate(self, *args):
    for _ in self.chunks[self.i]:
        label = Label(text='a', size_hint=(None, None), size=(10,10))
        self.label_container.add_widget(label)

    self.i += 1

    if self.i >= len(self.chunks):
        Clock.unschedule(self.iterate)

这是一种在组件生成速度和界面响应之间的折中。根据应用程序和执行环境的不同,调度间隔和n的不同值会产生最佳效果。

3

你可以通过Kivy的时钟模块,分批次地添加标签。

撰写回答