Kivy: управление жизненным циклом виджетов или увольнение их в GC - PullRequest
0 голосов
/ 11 апреля 2020

Вот приложение Hello World как с двумя кнопками. Большой заполняет список из 50 элементов, а маленький очищает его. Проверка использования памяти выполняется следующим образом:

$ mprof run --include-children python leak.py 
...
$ mprof plot --output out.png

Кроме kivy, требуется программное обеспечение kivymd, matplotlib и memory-profiler. Всякий раз, когда вы запускаете тест, создавая и затем очищая список несколько раз, нажимая кнопки, вы получаете график, похожий на непрерывно восходящую лестницу. Я абсолютно не знаю, что мне следует сделать, чтобы отправить удаленные виджеты сборщику мусора.

kivymd Не могу обойтись.

leak.py:

from functools import partial
import kivy

kivy.require("1.11.1")
from kivy.utils import platform

print(f"platform: {platform}")

from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout

from kivymd.app import MDApp
from kivymd.uix.tab import MDTabsBase
from kivymd.uix.list import TwoLineAvatarIconListItem
import gc


ACTION_ICON = "eye"


class PowerListItem(TwoLineAvatarIconListItem):
    """The engaged power supply item."""


class TabList(FloatLayout, MDTabsBase):
    """The engaged power supplies tab."""

    def surfacing(self, tab_text):
        pass

    def discover(self, cnt):
        ids = MDApp.get_running_app().root.ids
        for i in range(cnt):
            item = PowerListItem(
                text="List Item" + f" {i + 1:>2}", secondary_text="none"
            )
            ids.ps_list.add_widget(item)


class Contero(MDApp):

    def build(self):
        return Builder.load_file("leak.kv")

    def on_start(self):
        self.theme_cls.primary_palette = "Gray"

    def discovery_clean(self):
        for item in self.root.ids.ps_list.children:
            print(f"item: {item.text}")
        self.root.ids.ps_list.clear_widgets()
        gc.collect()

    def discovery_request(self, item_count=50):
        self.discovery_clean()

        tab_list = self.root.ids.ps_tab_list

        tab_list.discover(item_count)

    def on_tab_switch(self, instance_tabs, instance_tab, instance_tab_label, tab_text):
        """Called when switching tabs.

        :type instance_tabs: <kivymd.uix.tab.MDTabs object>;
        :param instance_tab: <__main__.Tab object>;
        :param instance_tab_label: <kivymd.uix.tab.MDTabsLabel object>;
        :param tab_text: text or name icon of tab;
        """

        instance_tab.surfacing(tab_text)


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

leak.kv:

#:kivy 1.11.1
#:import ACTION_ICON __main__.ACTION_ICON
BoxLayout:
    orientation: "vertical"
    MDTabs:
        id: ps_tabs
        on_tab_switch: app.on_tab_switch(*args)
        TabList:
            id: ps_tab_list
            text: "flash"
            ScrollView:
                bar_margin: 5
                bar_width: 15
                bar_color: .0, .8, .0, 1
                bar_inactive_color: .5, .5, .5, 1
                effect_cls: "ScrollEffect"
                scroll_type: ["bars", "content"]
                scroll_distance: 5
                scroll_timeout: 50
                MDList:
                    id: ps_list
    MDBottomAppBar:
        MDToolbar:
            id: ps_toolbar
            title: "Destroy a list"
            icon: ACTION_ICON
            type: "bottom"
            on_action_button: app.discovery_request()
            right_action_items: [ ['minus-box-outline', lambda x: app.discovery_clean()], ]


<PowerListItem>:
    IconLeftWidget:
        id: item_left
        icon: "earth"
...