'NoneType'对象的简单Kivy DropDown错误没有属性'bind'

时间:2017-06-28 01:32:46

标签: python python-2.7 user-interface kivy

我已经在Ubuntu 14.04上使用kivy 1.7.2一段时间了;非常容易使用,kv语言使设计变得轻而易举......

...除了DropDown已被证明几乎无法在不使用kv运行的情况下进行设置,甚至在使用更多python代码启动时也会出现问题。

这是我开始的......

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown


kv_text = """#:kivy 1.7.2
<MyWidget>
    DropDown:
        id: menupanel
        on_parent: self.dismiss()
        on_select: lambda: None
        Button:
            id: button_a
            size_hint_y: None
            text: "A"
        Button:
            id: button_b
            size_hint_y: None
            text: "B"
    Button:
        id: button_opendd
        pos_hint: {"top":1, "right":1}
        on_release: root.ids.menupanel.open(self)
        text: "open"
"""

class MyWidget(FloatLayout):
    def __init__(self, **kwargs):
        super(FloatLayout, self).__init__(**kwargs)

class MyWidgetApp(App):
    def build(self):
        return MyWidget()

def main():
    Builder.load_string(kv_text)
    app = MyWidgetApp()
    app.run()

if __name__ == '__main__':
    main()

... ....产生这个错误.... ...

 Traceback (most recent call last):
   File "wtfkivy.py", line 60, in <module>
     main()
   File "wtfkivy.py", line 57, in main
     app.run()
   File "/usr/lib/python2.7/dist-packages/kivy/app.py", line 577, in run
     root = self.build()
   File "wtfkivy.py", line 52, in build
     return MyWidget()
   File "wtfkivy.py", line 48, in __init__
     super(FloatLayout, self).__init__(**kwargs)
   File "/usr/lib/python2.7/dist-packages/kivy/uix/layout.py", line 61, in __init__
     super(Layout, self).__init__(**kwargs)
   File "/usr/lib/python2.7/dist-packages/kivy/uix/widget.py", line 163, in __init__
     Builder.apply(self)
   File "/usr/lib/python2.7/dist-packages/kivy/lang.py", line 1429, in apply
     self._apply_rule(widget, rule, rule)
   File "/usr/lib/python2.7/dist-packages/kivy/lang.py", line 1531, in _apply_rule
     child = cls(__no_builder=True)
   File "/usr/lib/python2.7/dist-packages/kivy/uix/dropdown.py", line 160, in __init__
     self.container.bind(minimum_size=self._container_minimum_size)
 AttributeError: 'NoneType' object has no attribute 'bind'

DropDown的self.container应默认为GridLayout,但在通过kv文件设置时不会发生这种情况。

我通过将kv中的DropDown:更改为MyDropDown:并将<MyDropDown>:定义放在所有内容之下来解决此错误。然后,在python中,我不得不用super(MyDropDown,self).__init__()添加类但是!它只是将一组重复的小部件放入下拉列表中。 Woops? (如果我将该init更改为__init__(**kwargs),我最终会遇到相同的绑定错误。嗯?)

我诚实地在互联网上搜索了很多次DropDown主要通过kv设置的好例子,但没有真正达到标记的。

即使python中的实现成功(使用super(),也不是首选。我需要DropDown的小部件才能访问主app小部件的属性。

所以问题是:是否有一种方法几乎完全在kv中实现DropDown(这使得设计如此干净和可访问)或其他仍然可以使主应用程序小部件可访问的东西?

修改 FJSevilla 所示并通过测试验证,DropDown在1.7.2版本中已被破坏。我添加了一个详细解答,因为许多其他人可能正在使用他们的发行版提供的版本。

2 个答案:

答案 0 :(得分:1)

在Kivy语言中使用DropDown并不太直观。但是,可以使用仅限kv语言创建自己的DropDown菜单:

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

kv_text = """\
#:kivy 1.7.2
<MyWidget>
    Widget:
        on_parent: menupanel.dismiss()

    DropDown:
        id: menupanel
        on_select: button_opendd.text = '{}'.format(args[1])

        Button:
            text: 'A'
            size_hint_y: None
            height: 30
            on_release: menupanel.select('A')

        Button:
            text: 'B'
            size_hint_y: None
            height: 30
            on_release: menupanel.select('B')

    Button:
        id: button_opendd
        pos_hint: {"top":1, "right":1}
        text: 'Press'
        on_release: menupanel.open(self)
        size_hint_y: None
        height: 40
"""

class MyWidget(FloatLayout):
    def __init__(self, **kwargs):
        super(MyWidget, self).__init__(**kwargs)

class MyWidgetApp(App):
    def build(self):
        return MyWidget()

def main():
    Builder.load_string(kv_text)
    app = MyWidgetApp()
    app.run()

if __name__ == '__main__':
    main()
  

警告:此代码在Kivy 1.9.3和Kivy 1.10.0中进行了测试,我不知道它是否会向后兼容1.7版。

如果您将on_parent事件放在DropDown实例中,则代码在Kivy 1.9中正确运行但在Kivy 1.10中失败(菜单始终隐藏)。

运行示例:

enter image description here

答案 1 :(得分:0)

DropDown(可能还有其他小部件)在Kivy 1.7中被破坏了。 DropDown具体无法从kv代码正确启动。

如果有人需要使用旧版本,请单独使用python实现下拉菜单,而不是使用kv(并预期会遇到麻烦):

dd = DropDown()
b1 = Button(text="a")
b2 = Button(text="b")
dd.add_widget(b1)
dd.add_widget(b2)

或尝试将升级所需的kivy至少1.9.x。 (我没有测试1.8。) 首先,如果你使用较旧的发行版,你可能需要将kivy升级到你的发行版提供的范围之外。

在Ubuntu 14.04上:

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A863D2D6
sudo add-apt-repository ppa:kivy-team/kivy-daily
sudo apt-get update
sudo apt-get install python-kivy python3-kivy

请注意,此升级似乎会更改某些行为(读取:新错误)...

首先改变......

对于DropDown,on_parent:self.dismiss()是DropDown本身的绑定属性。升级后,这导致DropDown在打开时立即解除自身。 (你看它只闪烁一瞬间。)

如果您现在遇到此错误,请移动menupanel.dismiss()(用您的id代替menupanel)替换根小部件可以缓解此问题。

第二次改变......

不知何故,在根小部件上使用menupanel.dismiss() OR self.ids.menupanel.dismiss()会破坏refs到下拉列表(基本上删除强引用/垃圾收集)并导致:

     on_release: menupanel.open(self)
File "weakproxy.pyx", line 30, in kivy.weakproxy.WeakProxy.__getattr__ (kivy/weakproxy.c:1145)
File "weakproxy.pyx", line 26, in kivy.weakproxy.WeakProxy.__ref__ (kivy/weakproxy.c:1069)
ReferenceError: weakly-referenced object no longer exists

(注意:我在互联网上看到这个错误的唯一一次是针对命名类(即由class MyButton(Button)代码实例化的kv。这只是一个普通的通用DropDown,所以为什么会导致问题在这里?)

如果收到 错误,解决方案是在Dropdown实例的任何地方添加对任何对象的引用,在实例化时或在打开DropDown的函数中添加引用。它必须在删除其他引用之前发生。 (在我的情况下,在切换到新Screen并返回时删除。)

class ...
    def ...
        self.backup_reference = self.ids.menupanel

为什么呢?我不知道。

....或者,如果您的DropDown非常简单,请尝试使用1 Spinner