Kivy:在添加大量小部件时可以使UI响应吗?
我有一个应用程序,需要动态添加很多小部件。这里有一个可以模拟这个功能的工作示例:
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_labels
和threaded_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的时钟模块,分批次地添加标签。