Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Kivy widgets creation really so slow or am I doing it wrong?

I've noticed that the widgets creation in my app is slow so I've created this simple test app to check it. It contains two screens and each of them contains simple list of widgets.

App:

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock

from time import time

class ListScreen(Screen):

    items_box = ObjectProperty(None)

    def on_enter(self):
        start = time()
        for i in range(0,50):
            self.items_box.add_widget(ListItem('Item '+str(i)))
        self.items_box.bind(minimum_height=self.items_box.setter('height'))
        print time()-start

    def on_leave(self):
        self.items_box.clear_widgets()

class ListItem(BoxLayout):

    title = StringProperty('')

    def __init__(self, title, **kwargs):
        super(ListItem, self).__init__(**kwargs)
        self.title = title

class ListApp(App):

    sm = ScreenManager()
    screens = {}

    def build(self):
        self.__create_screens()
        ListApp.sm.add_widget(ListApp.screens['list1'])
        Clock.schedule_interval(self._switch, 1)
        return ListApp.sm

    def _switch(self, *args):
        ListApp.sm.switch_to(ListApp.screens['list1' if ListApp.sm.current != 'list1' else 'list2'])

    def __create_screens(self):
        ListApp.screens['list1'] = ListScreen(name='list1')
        ListApp.screens['list2'] = ListScreen(name='list2')

if __name__ == '__main__':
    ListApp().run()

list.kv file:

<ListScreen>:
    items_box: items_box
    BoxLayout:
        orientation: "vertical"
        AnchorLayout:
            size_hint_y: 0.1
            padding: self.width*0.1, self.height*0.05
            Label:
                font_size: root.height*0.05
                text: "Some list"
        ScrollView:
            size_hint_y: 0.9
            size: self.size
            BoxLayout:
                id: items_box
                orientation: "vertical"
                padding: self.width*0.1, 0
                size_hint_y: None

<ListItem>:
    orientation: "horizontal"
    size_hint_y: None
    height: app.sm.height*0.1
    Label:
        font_size: app.sm.height*0.025
        text: root.title
        size_hint_x: 0.9
        text_size: self.size
        valign: "middle"
    CheckBox
        size_hint_x: 0.1

Log on my device (Moto G):

11-28 11:44:09.525  1848  2044 I python  : 0.5793800354
11-28 11:44:10.853  1848  2044 I python  : 0.453143119812
11-28 11:44:12.544  1848  2044 I python  : 0.633069992065
11-28 11:44:13.697  1848  2044 I python  : 0.369570970535
11-28 11:44:14.988  1848  2044 I python  : 0.594089031219
11-28 11:44:16.017  1848  2044 I python  : 0.339677095413
11-28 11:44:17.315  1848  2044 I python  : 0.58710193634
11-28 11:44:18.403  1848  2044 I python  : 0.359042882919
11-28 11:44:19.741  1848  2044 I python  : 0.613200187683
11-28 11:44:20.820  1848  2044 I python  : 0.359098911285
11-28 11:44:22.139  1848  2044 I python  : 0.60958814621
11-28 11:44:23.199  1848  2044 I python  : 0.354372024536
11-28 11:44:24.538  1848  2044 I python  : 0.643312931061
11-28 11:44:25.606  1848  2044 I python  : 0.360656023026
11-28 11:44:26.995  1848  2044 I python  : 0.682018995285
11-28 11:44:28.140  1848  2044 I python  : 0.393831014633
11-28 11:44:29.470  1848  2044 I python  : 0.591700077057
11-28 11:44:30.525  1848  2044 I python  : 0.346837043762
11-28 11:44:31.818  1848  2044 I python  : 0.607005834579
11-28 11:44:32.877  1848  2044 I python  : 0.36404299736
11-28 11:44:34.149  1848  2044 I python  : 0.586351156235
11-28 11:44:35.195  1848  2044 I python  : 0.349910974503
11-28 11:44:36.484  1848  2044 I python  : 0.588956832886
11-28 11:44:37.547  1848  2044 I python  : 0.367785930634
11-28 11:44:38.886  1848  2044 I python  : 0.639610052109
11-28 11:44:40.007  1848  2044 I python  : 0.394254922867
11-28 11:44:41.464  1848  2044 I python  : 0.732916116714

Come on, really? Simple list of 50 rows with label and checkbox needs 0.5 seconds on average to be created? Or am I doing something wrong?

like image 829
Andrzej S. Avatar asked Oct 17 '22 02:10

Andrzej S.


1 Answers

The solution was found thanks to Alexander Taylor (one of Kivy developers). Here is his answer:

Widget creation is relatively slow, especially depending on what the widget contains.

If making big lists, like in your examples, you're usually better off using RecycleView. This optimises things to reuse a smaller number of widgets during the scrolling.

So I've tried.

App:

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.clock import Clock

from time import time

class ListScreen(Screen):

    recycle_view = ObjectProperty(None)
    items_box = ObjectProperty(None)

    def on_enter(self):
        start = time()
        for i in range(0,50):
            self.recycle_view.data.append({'title': 'item'+str(i)})
        print time()-start

    def on_leave(self):
        self.recycle_view.data = []

class ListApp(App):

    sm = ScreenManager()
    screens = {}

    def build(self):
        self.__create_screens()
        ListApp.sm.add_widget(ListApp.screens['list1'])
        Clock.schedule_interval(self._switch, 1)
        return ListApp.sm

    def _switch(self, *args):
        ListApp.sm.switch_to(ListApp.screens['list1' if ListApp.sm.current != 'list1' else 'list2'])

    def __create_screens(self):
        ListApp.screens['list1'] = ListScreen(name='list1')
        ListApp.screens['list2'] = ListScreen(name='list2')

if __name__ == '__main__':
    ListApp().run()

list.kv file:

<ListScreen>:
    recycle_view: recycle_view
    items_box: items_box
    BoxLayout:
        orientation: "vertical"
        AnchorLayout:
            size_hint_y: 0.1
            padding: self.width*0.1, self.height*0.05
            Label:
                font_size: root.height*0.05
                text: "Some list"
        RecycleView:
            id: recycle_view
            size_hint: 1, 0.9
            viewclass: "ListItem"
            RecycleBoxLayout:
                id: items_box
                orientation: "vertical"
                padding: self.width*0.1, 0
                default_size_hint: 1, None
                size_hint: 1, None
                height: self.minimum_height

<ListItem@BoxLayout>:
    orientation: "horizontal"
    size_hint: 1, None
    height: app.sm.height*0.1
    title: ''
    Label:
        font_size: app.sm.height*0.025
        text: root.title
        size_hint_x: 0.9
        text_size: self.size
        valign: "middle"
    CheckBox
        size_hint_x: 0.1

Log on the same device (Moto G):

11-29 13:11:58.196 13121 13203 I python  : 0.00388479232788
11-29 13:11:59.192 13121 13203 I python  : 0.00648307800293
11-29 13:12:00.189 13121 13203 I python  : 0.00288391113281
11-29 13:12:01.189 13121 13203 I python  : 0.00324606895447
11-29 13:12:02.194 13121 13203 I python  : 0.00873804092407
11-29 13:12:03.188 13121 13203 I python  : 0.00265002250671
11-29 13:12:04.209 13121 13203 I python  : 0.00614500045776

More than 100 times faster! It's awesome.

like image 113
Andrzej S. Avatar answered Oct 21 '22 00:10

Andrzej S.