The Doctor
The Doctor

Reputation: 27

KivyMD RecycleView has low FPS & lags

I am using Python 3.8.12, Kivy 2.0.0 & KivyMD-0.104.2 I am running my app on PyCharm on Ubuntu 18.04 LTS on a ASUS ROG STRIX GL503VD. My issue is that every time i use a for loop to generate content in KivyMD BottomNavgation using RecycleView, no matter what content (might be even a for i in range(100)), the app is starting to move very slow (when manually resizing it takes around 10 seconds to resize correctly). Also i have FPS under 50 on the tab in which i generated the data while in the empty ones i get @80 FPS. The issue doesn't seem to happen when i run lets say for i in range(100) in Kivy 2.0.0, but i would prefer to stick to KivyMD.

I've been looking in the last week on the internet for solutions but no luck and is a bit hard to understand kivy documentation regarding RecycleView.

Below is my code, please correct me if i did something out of "good practice". I just started to learn Kivy and i'm a beginner in Python (@1 year).

Thank you very much.

EDIT: Also when deployed to Android it works below 30 FPS on a Xiaomi Redmi Note8 Pro.

main.py

from kivy.lang import Builder
from kivymd.app import MDApp as md
from kivymd.uix.list import TwoLineListItem

import json as js
#from kivy.utils import platform
from kivy.core.window import Window

'''
if platform == "android":
    try:
        from android.permissions import request_permissions, Permission
        request_permissions([Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE])
    except:
        pass
'''

class MainApp(md):
    def build(self):
        Window.size = (491, 1064)
        self.theme_cls.primary_palette = 'Red'
        self.theme_cls.primary_hue = '50'
        layouts = Builder.load_file("layouts.kv")
        return layouts
    def on_start(self):
        for key, value in self.read_list().items():
            self.root.ids.container1.add_widget(TwoLineListItem(text=f"{key}", secondary_text="Tests : {}".format(len(value))))
        self.fps_monitor_start()
    def read_list(self):
        with open('tests.json', 'r') as read_test:
            #82 lines in test_list dict
            test_list = js.load(read_test)
        return test_list


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

layouts.kv

<MainLayout>
#:import get_color_from_hex kivy.utils.get_color_from_hex
MDGridLayout:
    cols: 3
    size_hint: 1, 1
    MDBottomNavigation:
        panel_color: get_color_from_hex("#008080")
        text_color_active: get_color_from_hex("#FFFFFF")
        MDBottomNavigationItem:
            name: 'Item1'
            text: 'item_1'
            icon: 'language-python'
            MDGridLayout:
                cols: 1
                RecycleView:
                    MDList:
                        id: container1

        MDBottomNavigationItem:
            name: 'Item2'
            text: 'item_2'
            icon: 'language-cpp'
            MDBoxLayout:
                RecycleView:
                    MDList:
                        id: container2


        MDBottomNavigationItem:
            name: 'Item3'
            text: 'item_3'
            icon: 'language-javascript'
            MDBoxLayout:
                RecycleView:
                    MDList:
                        id: container3


Upvotes: 0

Views: 518

Answers (1)

John Anderson
John Anderson

Reputation: 38992

You are not actually using the RecycleView. To use a RecycleView, you must have a RecycleLayout as its child, a viewclass, and a data list that indicates the items in the RecycleLayout.

Here is one way to use the RecycleView in your App. First, modify the kv to indicate the RecycleLayout and the viewclass:

MDGridLayout:
    cols: 3
    size_hint: 1, 1
    MDBottomNavigation:
        panel_color: get_color_from_hex("#008080")
        text_color_active: get_color_from_hex("#FFFFFF")
        MDBottomNavigationItem:
            name: 'Item1'
            text: 'item_1'
            icon: 'language-python'
            MDGridLayout:
                cols: 1
                RecycleView:
                    id: container1
                    viewclass: 'TwoLineListItem'
                    RecycleBoxLayout:
                        default_size: None, dp(75)
                        default_size_hint: 1, None
                        size_hint_y: None
                        height: self.minimum_height
                        orientation: 'vertical'

The above moves the container1 id to the RecycleView and adds the needed properties to allow the RecycleView to operate.

Then the on_start() method can be modified to create the data list for the RecycleView:

def on_start(self):
    data = []
    for key, value in self.read_list().items():
        data.append({'text': f"{key}", 'secondary_text': "Tests : {}".format(len(value))})
    self.root.ids.container1.data = data
    self.fps_monitor_start()

Upvotes: 1

Related Questions