Kivy 在 GridLayout 中添加和删除小部件会产生弱引用问题

Kivy adding and removing widgets from GridLayout gives weak reference issue

我正在尝试使用 Python-Kivy 在 GridLayout 中动态添加和删除小部件,但我 运行 遇到了弱引用问题。当包含 GridLayout 的屏幕被初始化时,我在 GridLayout 中放置了一个标签,其中包含通知用户容器没有项目的文本(从技术上讲,它在那里有标签,所以它不一定是空的)。然后我有一个 Button,允许用户将单独的 GridLayout 小部件添加到包含 Label、TextInput 和 CheckBox 的 GridLayout,还有一个 Button,允许用户删除他们创建的那些单独的 GridLayout 小部件。如果所有这些小部件(由用户动态添加)都被删除,那么原始标签将添加回 GridLayout。

当我尝试在 Python 中构建此逻辑以与 Kivy 配对时,我 运行 遇到了一个问题,即原始标签似乎永远不会从堆栈中完全删除。

我的印象是 self.ids.widget_list.remove_widget(self.ids.empty) 会删除 ID 为 empty 的标签小部件,但事实并非如此。很明显,当我调用 print(self.ids):

时,小部件仍然存在

{'widget_list': <WeakProxy to <kivy.uix.gridlayout.GridLayout object at 0x0451F030>>, 'empty': <WeakProxy to <kivy.uix.gridlayout.GridLayout object at 0x0451F9D0>>}

非常感谢任何帮助。

编辑

每次调用 remove() 方法时检查 for i in self.layouts: print(i.children) 时,表明对添加的小部件的引用永远不会被完全删除。这可能是我的问题所在,但不确定如何解决它。

Python

import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.checkbox import CheckBox
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen

#Load kv file
Builder.load_file('test.kv')

#First Screen
class Screen1(Screen):

    layouts = []

    def remove(self):

        for i in self.layouts:
            if i.children[0].active:
                self.ids.widget_list.remove_widget(i)

        if len(self.layouts)==0:
            layout = GridLayout(rows=1, id=empty)
            layout.add_widget(Label(text='Nothing Here'))
            self.ids.widget_list.add_widget(layout)
        else:
            self.update_hints()

    def add(self):

        if len(self.ids.widget_list.children)<5:

            print(self.ids)
            self.ids.widget_list.remove_widget(self.ids.empty)
            print(self.ids)
            layout = GridLayout(cols=3)
            layout.add_widget(Label(text='Test ' + str(len(self.ids.widget_list.children)+1)))
            layout.add_widget(TextInput())
            layout.add_widget(CheckBox())
            self.ids.widget_list.add_widget(layout)

            self.layouts.append(layout)
            self.update_hints()

        else:

            layout = GridLayout(cols=1)
            layout.add_widget(Label(text='Only five allowed at once.\nRemove at least one to add another.'))
            button = Button(text='Acknowledge'); layout.add_widget(button)
            popup = Popup(content=layout, title='Limit Reached', size_hint=(.5,.5), auto_dismiss=False)
            button.bind(on_release=popup.dismiss)
            popup.open()

    def update_hints(self):

        for i in self.layouts:
            i.children[1].hint_text = 'Ex. ' + str(round(100/len(self.ids.widget_list.children),2)) + '%'

#Initialize Screens and Start App
class MyScreenManager(ScreenManager):

    pass

#Main application
class SampleApp(App):

    def build(self):
        self.sm = MyScreenManager()
        return self.sm

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

kv

<MyScreenManager>:

    Screen1:
        name: 'screen1'

<Screen1>:

    BoxLayout:

        orientation: 'vertical'

        GridLayout:

            cols: 3
            padding: 20
            spacing: 20
            size_hint: 1, .1

            Label:
                text: 'List of Widgets'
                underline: True
            Label:
                text: 'Percentage'
                underline: True
            Label:
                text: 'Remove (Y/N)'
                underline: True

        ScrollView:

            size_hint: 1, .5
            do_scroll_x: False
            padding: 20
            spacing: 20

            GridLayout:

                id: widget_list
                cols: 1
                spacing: 5

                GridLayout:
                    id: empty
                    rows: 1
                    Label:
                        text: 'Nothing Here'

        GridLayout:

            cols: 3
            padding: 20
            spacing: 20
            size_hint: 1, .2

            Button:
                text: 'Add Widget'
                on_release: root.add()

            Label:
                text: ''

            Button:
                text: 'Remove Widget'
                on_release: root.remove()

好的,我似乎找到了一种更简单的实现方式,可以避免为初始标签指定任何 id(与显示 if self.layouts==[] 的标签相同):

Python

import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.checkbox import CheckBox
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen

#Load kv file
Builder.load_file('test.kv')

#First Screen
class Screen1(Screen):

    count = 0
    layouts = []

    def remove(self):

        for i in self.layouts:
            if i.children[0].active:
                self.ids.widget_list.remove_widget(i)

        self.layouts = [i for i in self.layouts if not i.children[0].active]

        if self.layouts!=[]:
            self.update_hints()
        else:
            layout = GridLayout(rows=1)
            layout.add_widget(Label(text='Nothing Here'))
            self.ids.widget_list.add_widget(layout)

    def add(self):

        if self.layouts==[]:
            self.ids.widget_list.clear_widgets()

        if len(self.ids.widget_list.children)<5:
            self.count+=1
            layout = GridLayout(cols=3)
            layout.add_widget(Label(text='Test ' + str(self.count)))
            layout.add_widget(TextInput())
            layout.add_widget(CheckBox())
            self.ids.widget_list.add_widget(layout)
            self.layouts.append(layout)
            self.update_hints()
        else:
            layout = GridLayout(cols=1)
            layout.add_widget(Label(text='Only five allowed at once.\nRemove at least one to add another.'))
            button = Button(text='Acknowledge'); layout.add_widget(button)
            popup = Popup(content=layout, title='Limit Reached', size_hint=(.5,.5), auto_dismiss=False)
            button.bind(on_release=popup.dismiss)
            popup.open()

    def update_hints(self):

        for i in self.layouts:
            i.children[1].hint_text = 'Ex. ' + str(round(100/len(self.ids.widget_list.children),2)) + '%'

#Initialize Screens and Start App
class MyScreenManager(ScreenManager):

    pass

#Main application
class SampleApp(App):

    def build(self):
        self.sm = MyScreenManager()
        return self.sm

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

kv

<MyScreenManager>:

    Screen1:
        name: 'screen1'

<Screen1>:

    BoxLayout:

        orientation: 'vertical'

        GridLayout:

            cols: 3
            padding: 20
            spacing: 20
            size_hint: 1, .1

            Label:
                text: 'List of Widgets'
                underline: True
            Label:
                text: 'Percentage'
                underline: True
            Label:
                text: 'Remove (Y/N)'
                underline: True

        ScrollView:

            size_hint: 1, .5
            do_scroll_x: False
            padding: 20
            spacing: 20

            GridLayout:

                id: widget_list
                cols: 1
                spacing: 5

                GridLayout:
                    rows: 1
                    Label:
                        text: 'Nothing Here'

        GridLayout:

            cols: 3
            padding: 20
            spacing: 20
            size_hint: 1, .2

            Button:
                text: 'Add Widget'
                on_release: root.add()

            Label:
                text: ''

            Button:
                text: 'Remove Widget'
                on_release: root.remove()