【问题标题】:Large set of widgets in kivy - optimizationkivy 中的大量小部件 - 优化
【发布时间】:2021-10-17 17:33:51
【问题描述】:

我有一个用 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()

【问题讨论】:

    标签: python python-3.x optimization kivy kivy-language


    【解决方案1】:

    您可以使用 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()
    

    图片:

    【讨论】:

    • 嗨,谢谢你的回答,但是当我运行你的代码时,我得到一个 TypeError 说:TypeError: '&gt;' not supported between instances of 'str' and 'int'
    • 好吧,你看到类型错误所在的行号了吗?这很奇怪,因为我的示例没有使用像 if "hi" &gt; 6 这样的条件,这会抛出该错误类型
    • 我修好了,recycleview的default_size有问题
    猜你喜欢
    • 1970-01-01
    • 2021-06-08
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多