首页 > 解决方案 > kivy 中的大量小部件 - 优化

问题描述

我有一个用kivymd编写的音乐应用程序,它会扫描您的默认下载目录并提取有关您的音频文件的信息。(基本上是检索IDV3标签和其他东西)

我的桌面上最多只有 20 个音频文件,这应该不是问题,但我决定用许多音频(如 120 个)文件测试我的程序,结果很糟糕

界面太慢了,几乎无法正常工作。我应该如何在 kivy 中显示大量小部件而不会导致这种灾难性的性能下降?

另外,我怎样才能使小部件的加载异步,以便它们在启动时也不会占用大量时间?

我的代码的最小可重现示例:

import os
from kivy.lang import Builder
from kivy.uix.behaviors import ButtonBehavior
from kivy.properties import ObjectProperty
from kivymd.uix.behaviors import RectangularRippleBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.app import MDApp


class Song:
    """Class for extracting information about an audio file."""
    def __init__(self, path: str):
        self._path = path
        self._title = "My Song"
        self._artist = "Unknown artist"
        # the album cover is going to a texture retrieved from the file itself
        # but in this case, I will refer to a file.
        self._album_cover = "Default-Album-Cover.jpg"

    @property
    def path(self):
        return self._path

    @property
    def title(self):
        return self._title

    @property
    def artist(self):
        return self._artist

    @property
    def album_cover(self):
        return self._album_cover


class SongCard(ButtonBehavior, RectangularRippleBehavior, MDBoxLayout):
    """Custom widget for creating cards."""
    song_obj = ObjectProperty(Song("dummy song"), rebind=True)


class MainApp(MDApp):
    def __init__(self, **kwargs):
        super(MainApp, self).__init__(**kwargs)
        self.theme_cls.theme_style = "Dark"
        self.kv = Builder.load_string('''
#:kivy 2.0.0
<SongCard>:
    orientation: "vertical"
    size_hint_y: None
    height: "300dp"
    radius: ("10dp",)
    canvas.before:
        Color:
            rgba: app.theme_cls.bg_dark
        RoundedRectangle:
            pos: self.pos
            size: self.size
            radius: root.radius
    FitImage:
        source: root.song_obj.album_cover
    MDLabel:
        text: root.song_obj.title
        adaptive_height: True
    MDLabel:
        text: root.song_obj.artist
        adaptive_height: True
ScrollView:
    do_scroll_x: False
    MDGridLayout:
        id: song_grid
        cols: 2
        adaptive_height: True
        spacing: "10dp"
        padding: "10dp"
''')

    def build(self):
        return self.kv

    def on_start(self):
        for _ in range(25):
            for audio_file in os.listdir(os.path.join(os.path.expanduser('~'), "Downloads")):
                if audio_file.endswith(".mp3"):
                    song_obj = Song(audio_file)
                    self.kv.ids.song_grid.add_widget(
                        SongCard(
                            song_obj=song_obj
                        )
                    )


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

标签: pythonpython-3.xoptimizationkivykivy-language

解决方案


您可以使用 RecycleView 类来管理小部件的数量并帮助您的程序能够接受大量的小部件,ScrollView 很棒但是 recycleview 将数据作为字典管理,然后您可以使用 BoxLayout 小部件下的两个 BoxLayout 类具有 GridLayout 类功能,我用 213 个元素对此进行了测试,它适用于许多小部件和良好的性能:

import os
from kivy.lang import Builder
from kivy.uix.behaviors import ButtonBehavior
from kivy.properties import ObjectProperty, StringProperty
from kivymd.uix.behaviors import RectangularRippleBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.app import MDApp
from kivy.uix.recyclegridlayout import RecycleGridLayout



class Song:
    """Class for extracting information about an audio file."""
    def __init__(self, path: str):
        self._path = path
        self._title = "My Song"
        self._artist = "Unknown artist"
        # the album cover is going to a texture retrieved from the file itself
        # but in this case, I will refer to a file.
        self._album_cover = "Default-Album-Cover.jpg"

##    @property
##    def path(self):
##        return self._path
##
##    @property
##    def title(self):
##        return self._title
##
##    @property
##    def artist(self):
##        return self._artist
##
##    @property
##    def album_cover(self):
##        return self._album_cover


class SongCard(MDBoxLayout):
    """Custom widget for creating cards."""
    #song_obj = ObjectProperty(Song("dummy song"), rebind=True)
    song_obj = StringProperty()

class B2(ButtonBehavior, RectangularRippleBehavior, MDBoxLayout):
    """Custom widget for creating cards."""
    pass


class MainApp(MDApp):
    def __init__(self, **kwargs):
        super(MainApp, self).__init__(**kwargs)
        self.theme_cls.theme_style = "Dark"
        self.kv = Builder.load_string('''
#:kivy 2.0.0
<SongCard>:
    orientation: "vertical"
    size_hint_y: None
    height: "300dp"
    #radius: ("10dp",)
    BoxLayout:
        padding: dp(10)
        spacing: dp(5)
        B2:
            padding: dp(10)
            canvas.before:
                Color:
                    rgba: [0,1,0,.2]
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [30,]
            orientation: "vertical"
            spacing: dp(5)
            FitImage:
                size_hint: 1,1
                source: "Help5/cc.png" #root.song_obj.album_cover
            MDLabel:
                text: root.song_obj[:40] #.title
                adaptive_height: True
            MDLabel:
                text: " - jbsidis"  #.artist
                adaptive_height: True
                
        B2:
            padding: dp(10)
            canvas.before:
                Color:
                    rgba: [1,0,0,.2]
                RoundedRectangle:
                    pos: self.pos
                    size: self.size
                    radius: [30,]
            orientation: "vertical"
            spacing: dp(5)
            FitImage:
                source: "Help5/cc.png" #root.song_obj.album_cover
            MDLabel:
                text: root.song_obj[:40] #.title
                adaptive_height: True
            MDLabel:
                text: " - jbsidis"   #.artist
                adaptive_height: True

Screen:
    BoxLayout:
        orientation: "vertical"
        #do_scroll_x: False
        RecycleSupportGridLayoutjbsidis:
            id: song_grid
            key_viewclass: 'viewclass'
            key_size: 'height'

            RecycleBoxLayout:
                padding: dp(10)
                default_size: None, dp(248)
                default_size_hint: 1, None
                size_hint_y: None
                height: self.minimum_height
                orientation: 'vertical'


<RecycleSupportGridLayoutjbsidis@RecycleView>:

''')

    def build(self):
        return self.kv

    def on_start(self):
        #for _ in range(25):
        for audio_file in os.listdir("/media/jbsidis/Android/Q/D/Anthony Robbins/"): #os.path.join(os.path.expanduser('~'), "Downloads")
            #if audio_file.endswith(".mp3"):
            #song_obj = Song(audio_file)
            self.kv.ids.song_grid.data.append(
                {
                    "viewclass": "SongCard",
                    "song_obj": audio_file
                    }
                )


##                {
##                    "viewclass": "CustomOneLineIconListItem",
##                    "icon": name_icon,
##                    "text": name_icon,
##                    "on_release": lambda:Clipboard.copy(name_icon),
##                }


##                    self.kv.ids.song_grid.add_widget(
##                        SongCard(
##                            song_obj=song_obj
##                        )
##                    )


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

图片: 在此处输入图像描述


推荐阅读