Kivy DropDown 实现在调用 <DropDown instance>.open(self) 时出现错误 "Cannot add <object> to window, it already has a parent <object>"

Kivy DropDown implementation has error "Cannot add <object> to window, it already has a parent <object>" when calling <DropDown instance>.open(self)

我的目标是一个类似于 Windows 文件资源管理器中看到的下拉列表:

我正在使用以下代码试验 DropDown

main.py

import kivy
kivy.require('2.0.0')

from kivy.app import App
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.textinput import TextInput
from kivy.properties import ObjectProperty


class CustomDropDown(TextInput):

    droplist = ObjectProperty(None)
    
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            self.droplist.open(self)
        return super().on_touch_down(touch)


class MainApp(AnchorLayout):
    pass

class MyApp(App):

    def build(self):
        return MainApp()


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

my.kv

<MainApp>:
    CustomDropDown:

<CustomDropDown>
    text: 'Select an option..'
    size_hint_y: None
    height: 30

    droplist: options

    DropDown:
        id: options
        Label:
            text: 'Option 1'
        Label:
            text: 'Option 2'
        Label:
            text: 'Option 3'

我在 self.droplist.open(self) 行收到以下错误:

Cannot add <kivy.uix.dropdown.DropDown object at 0x00000223791F39E0> to window, it already has a parent <__main__.CustomDropDown object at 0x00000223791BA580>

认为 这应该有效,经过一段时间的故障排除后,我仍然无法弄清楚为什么它不起作用。有人发现我的错误吗?

--2021 年 9 月 12 日更新 3:30PM--

感谢@inclement 的建议,多亏了你的回复,我才能继续前进。但是,我现在有一个后续问题:

为了实施该建议,我对 DropDownLabel 进行了子分类,并分别覆盖了 on_selecton_touch_down 方法。我还必须在我的子类中覆盖 DropDown__init__ 方法,以便我可以访问 TextInput 框并使用 on_select 更新其内容。这看起来很尴尬并且添加了很多代码,我是否遗漏了一些可以减少此类代码的数量和复杂性的东西?

已更新main.py

import kivy
kivy.require('2.0.0')

from kivy.app import App
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput


class CustomDropDown(TextInput):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.dropdown = DropDownList(self)

        for item in ['Option 1', 'Option 2', 'Option 3']:
            lbl = DropDownItem(text=item, size_hint_y=None, height=30)
            self.dropdown.add_widget(lbl)
        
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            self.dropdown.open(self)
        return super().on_touch_down(touch)


class DropDownList(DropDown):

    def __init__(self, user_choice, **kwargs):
        super().__init__(**kwargs)
        self.user_choice = user_choice

    def on_select(self, data):
        setattr(self.user_choice, 'text', data)


class DropDownItem(Label):

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            dropdown = self.parent.parent
            dropdown.select(self.text)
        return super().on_touch_down(touch)
            

class MainApp(AnchorLayout):
    pass

class MyApp(App):

    def build(self):
        return MainApp()


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

已更新my.kv

<MainApp>:
    DropDownTextInput:

<DropDownTextInput>
    text: 'Select an option..'
    size_hint_y: None
    height: 30

再次感谢!

DropDowns 设计为通过调用它们的 open 方法打开,该方法将它们添加到小部件树中,使它们出现在所有内容的顶部并具有正确的位置,但你已经添加了它通过将其放入您的 kv 规则来自己添加到小部件树。这就是为什么它已经有了 parent。

您不应将其声明为 CustomDropDown 的 child,而是创建一个新实例并调用该实例的 open 方法。