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 的建议,多亏了你的回复,我才能继续前进。但是,我现在有一个后续问题:
为了实施该建议,我对 DropDown
和 Label
进行了子分类,并分别覆盖了 on_select
和 on_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
方法。
我的目标是一个类似于 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 的建议,多亏了你的回复,我才能继续前进。但是,我现在有一个后续问题:
为了实施该建议,我对 DropDown
和 Label
进行了子分类,并分别覆盖了 on_select
和 on_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
方法。