当我期待一个 TextInput 实例时,Kivy ObjectProperty 是 None,我错过了什么?

Kivy ObjectProperty is None when I'm expecting a TextInput instance, what am I missing?

我正在尝试使用 DropDown 构建一个下拉列表,该小部件的行为应该与 windows 文件资源管理器中看到的类似。

我有一个 TextInput 和一个 Button,我将 DropDown 与之相关联。当我 运行 以下代码时,出现错误:

Exception has occurred: AttributeError
'NoneType' object has no attribute 'text'
  File "C:\Users\hanne\Documents\_Educational\_Learning\Python\Kivy\DropDown\B.ii\main.py", line 34, in on_select
    setattr(self.user_choice, 'text', data)
  File "C:\Users\hanne\Documents\_Educational\_Learning\Python\Kivy\DropDown\B.ii\main.py", line 45, in on_touch_down
    self.dropdown.select(self.text)
  File "C:\Users\hanne\Documents\_Educational\_Learning\Python\Kivy\DropDown\B.ii\main.py", line 69, in <module>
    MyApp().run()

main.py

import kivy
kivy.require('2.0.0')

from kivy.app import App
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.properties import ObjectProperty

class TextEntryDropDown(BoxLayout):

    user_choice = ObjectProperty(None)
    drop_button = ObjectProperty(None)

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

        self.dropdown = DropDownList(self.user_choice)

        for item in ['Option 1', 'Option 2', 'Option 3']:
            label = DropDownItem(self.dropdown, text=item, size_hint_y=None, height=30)
            self.dropdown.add_widget(label)


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 __init__(self, dropdown, **kwargs):
        super().__init__(**kwargs)
        self.dropdown = dropdown

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


class DropButton(Button):
    
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            dropdown = self.parent.dropdown
            dropdown.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>:
    anchor_x: 'left'
    anchor_y: 'top'
    TextEntryDropDown:
        

<TextEntryDropDown>
    size_hint_y: None
    height: 30

    user_choice: user_choice
    drop_button: drop_button

    TextInput:
        id: user_choice
        text: 'Select an option..'
        size_hint: (None, None)
        height: 30
        width: root.width - drop_button.width

    DropButton:
        id: drop_button
        size_hint: (None, None)
        height: user_choice.height
        width: user_choice.width

我试图追查为什么 user_choiceNone,我希望它是对 my.kv 中定义的 TextInput 的引用。知道为什么会这样吗?我不能把我的手指放在上面。

谢谢!

问题是TextEntryDropDownObjectPropertiesuser_choicedrop_button)在__init__()的方法中还不可用TextEntryDropDown。解决方法是在创建 DropDownList 时传递对 TextEntryDropDown 的引用,而不是传递尚未定义的 user_choice。像这样:

class TextEntryDropDown(BoxLayout):
    user_choice = ObjectProperty(None)
    drop_button = ObjectProperty(None)

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

        self.dropdown = DropDownList(self)  # pass reference to this TextEntryDropDown

        for item in ['Option 1', 'Option 2', 'Option 3']:
            label = DropDownItem(self.dropdown, text=item, size_hint_y=None, height=30)
            self.dropdown.add_widget(label)

然后在 DropDownList:

class DropDownList(DropDown):

    def __init__(self, ted, **kwargs):
        super().__init__(**kwargs)
        self.textEntryDropDown = ted  # save reference to the TextEntryDropDown
        # self.user_choice = user_choice

    def on_select(self, data):
        self.textEntryDropDown.user_choice.text = data  # use TextEntryDropDown to access now available user_choice ObjectProperty
        # setattr(self.user_choice, 'text', data)

问题是您在 .kv 文件的第 20 行创建了一个无限循环。在第 26 行中,您将 drop_button 宽度定义为 user_choice.width,然后在第 20 行中,您将 user_choice 宽度定义为 root.width - drop_button.width.

要修复错误,请更改以下内容:

width: root.width - drop_button.width

width: drop_button.width