Search code examples
pythonkivywidgetlagkivymd

Kivy widget generation very slow


I'm making a kivy app to find the rhyming words for a word entered by the user. It displays all the rhyming words as OneLineListItems in an MDList which is inside a kivy RecycleView. On clicking on one of these OneLineListItems it displays the definition of the word on the right-hand side of the screen. However, when I click on a OneLineListItem its definition takes very long to appear and sometimes it lags so badly that the app closes. Am I doing something wrong or is it just my computer? Code below:

from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.label import MDLabel
from kivymd.uix.list import OneLineListItem
import pronouncing
import enchant
from PyDictionary import PyDictionary

dictionary = PyDictionary()
d = enchant.Dict("en_US")

kv = """
Screen:
    input:input
    scroll:scroll
    word:word
    defs:defs
    MDGridLayout:
        rows: 1
        cols: 2
        MDGridLayout:
            rows: 2
            cols: 1
            MDFloatLayout:
                MDTextField:
                    id:input
                    size_hint: (.4, None)
                    height: 26
                    multiline: False
                    on_text_validate: app.rhyme()
                    hint_text: "Search"
                    mode: "rectangle"
                    pos_hint: {"center_x": .25, "center_y": .85}
                    font_name: "DejaVuSans.ttf"
                    text_size: self.size
            MDFloatLayout:
                RecycleView:
                    size_hint: 0.85,1.5
                    bar_width: dp(15)
                    bar_height: dp(40)
                    scroll_type: ["content"]
                    pos_hint: {"center_x": 0.45, "center_y": 0.93}
                    MDList:
                        id: scroll
                
        
        MDBoxLayout:
            id:defs
            orientation: "vertical"
            md_bg_color: 0, 1, 0.2, 1
            MDLabel:
                id: word
                text: ""
                text_size: self.size
                

"""


class RhymeApp(MDApp):
    played = []
    x_turn = True

    def build(self):
        self.screen = Builder.load_string(kv)
        return self.screen

    def rhyme(self):
        raw_rhymes = pronouncing.rhymes(self.screen.input.text)
        rhymes = []
        [rhymes.append(x) for x in raw_rhymes if x not in rhymes and x[-1] != "." and d.check(x)]
        self.screen.scroll.clear_widgets()
        for i in rhymes:
            self.screen.scroll.add_widget(
                OneLineListItem(text=f"{i}".capitalize(), on_release=self.dictionary)
            )

    def dictionary(self, btn):
        nl = '\n'
        self.screen.defs.clear_widgets()
        self.screen.word.text = btn.text.capitalize()
        meaning = dictionary.meaning(btn.text, disable_errors=True)
        if meaning is None:
            self.screen.defs.add_widget(
                MDLabel(text=f"Sorry, no meaning for that word.",
                        text_size="self.size")
            )
        else:
            for key in meaning:
                self.screen.defs.add_widget(
                    MDLabel(text=f"Part of speech: {key} {nl}Meaning: {nl}{nl}{meaning[key][0].capitalize()}.",
                            text_size="self.size")
                )


if __name__ == "__main__":
    RhymeApp().run()

Can someone please help?


Solution

  • First create a custom class for the data-class like following:

    class ListItem(OneLineListItem):
        # Here define all the necessary attrs., methods apart from the defaults (if you need any).
    

    Now in your .kv initialize RecycleView as,

                    RecycleView:
                        id: scroll
                        #size_hint: 0.85,1.5
                        bar_width: dp(15)
                        bar_height: dp(40)
                        scroll_type: ["content"]
                        #pos_hint: {"center_x": 0.45, "center_y": 0.93}
                        viewclass: "ListItem"
                        RecycleBoxLayout:
                            default_size: None, dp(48)
                            default_size_hint: 1, None
                            size_hint_y: None
                            height: self.minimum_height
                            orientation: 'vertical'
    

    Now you are ready to feed RecycleView with you data as,

        def rhyme(self):
            ...
            self.screen.ids.scroll.data = [
                {"text" : f"{i}".capitalize()}
            for i in rhymes]