Kivy:AttributeError,其属性在kv文件中定义

时间:2019-10-20 01:25:08

标签: python python-3.x kivy attributeerror

  • Python :3.6.8
  • Kivy :1.9.0
  • PyCharm :2019.2.3

在编写和测试本书中的源代码时,我正在阅读“ Kivy – Python第二版中的交互式应用程序和游戏”。

当我完成第3章时,我遇到了这个错误:

Exception ignored in: 'kivy.properties.observable_list_dispatch'
Traceback (most recent call last):
File "kivy/properties.pyx", line 579, in kivy.properties.Property.dispatch (/tmp/pip-install-rsswmpdy/kivy/kivy/properties.c:7216)
File "kivy/_event.pyx", line 1214, in kivy._event.EventObservers.dispatch (/tmp/pip-install-rsswmpdy/kivy/kivy/_event.c:14036)
File "kivy/_event.pyx", line 1120, in kivy._event.EventObservers._dispatch (/tmp/pip-install-rsswmpdy/kivy/kivy/_event.c:13194)
File "/home/madtyn/PycharmProjects/learning_kivy/comics/drawingspace.py", line 8, in on_children
    self.status_bar.counter = len(self.children)
AttributeError: 'DrawingSpace' object has no attribute 'status_bar'

我试图通过将我的代码与从书中下载的源代码进行比较来查找错误,但没有发现任何相关差异。在几乎相同的两个版本中,我不喜欢status_bar和DrawingSpace之间的关系有什么区别。

我在下面粘贴我的代码。我省略了这些文件:

  • 工具箱。*
  • comicwidgets。*
  • 一般选项。*

    因为我认为它们无关紧要,所以一切都比较容易。但是,如果有人提出要求或无法解决,我将按需粘贴它们。

comiccreator.py

import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.anchorlayout import AnchorLayout

kivy.require('1.9.0')

Builder.load_file('toolbox.kv')
Builder.load_file('drawingspace.kv')
Builder.load_file('comicwidgets.kv')
Builder.load_file('generaloptions.kv')
Builder.load_file('statusbar.kv')


class ComicCreator(AnchorLayout):
    pass


class ComicCreatorApp(App):
    def build(self):
        return ComicCreator()


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

comiccreator.kv

# File name: comiccreator.kv
#:kivy 1.9.0

<ComicCreator>:
    AnchorLayout:
        anchor_x: 'left'
        anchor_y: 'top'
        ToolBox:
            id: _tool_box
            drawing_space: _drawing_space
            comic_creator: root
            size_hint: None, None
            width: 100
    AnchorLayout:
        anchor_x: 'right'
        anchor_y: 'top'
        DrawingSpace:
            id: _drawing_space
            status_bar: _status_bar  # <====== Here we define the status_bar property!!!
            general_options: _general_options
            tool_box: _tool_box
            size_hint: None, None
            width: root.width - _tool_box.width
            height: root.height - _general_options.height - _status_bar.height
    AnchorLayout:
        anchor_x: 'center'
        anchor_y: 'bottom'
        BoxLayout:
            orientation: 'vertical'
            GeneralOptions:
                id: _general_options
                drawing_space: _drawing_space
                comic_creator: root
                size_hint: 1,None
                height: 48
            StatusBar:
                id: _status_bar
                size_hint: 1,None
                height: 24

drawingspace.py

# File name: drawingspace.py
from kivy.properties import ObjectProperty
from kivy.uix.relativelayout import RelativeLayout


class DrawingSpace(RelativeLayout):
    def on_children(self, instance, value):
        self.status_bar.counter = len(self.children)  # Here the error states that
                                                      # status_bar attr does not exist

drawingspace.kv

# File name: drawingspace.kv

#:kivy 1.9.0
#:import drawingspace drawingspace

<DrawingSpace@RelativeLayout>:
    canvas.before:
        Line:
            rectangle: 0, 0, self.width - 4, self.height - 4
    StickMan:

statusbar.py

# File name: statusbar.py
import kivy
kivy.require('1.9.0')

from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty, ObjectProperty


class StatusBar(BoxLayout):
    counter = NumericProperty(0)
    previous_counter = 0

    def on_counter(self, instance, value):
        if value == 0:
            self.msg_label.text = "Drawing space cleared"
        elif value - 1 == self.__class__.previous_counter:
            self.msg_label.text = "Widget added"
        elif value + 1 == StatusBar.previous_counter:
            self.msg_label.text = "Widget removed"
        self.__class__.previous_counter = value

statusbar.kv

# File name: statusbar.kv
#:kivy 1.9.0
#:import statusbar statusbar

<StatusBar@BoxLayout>:
    msg_label: _msg_label
    orientation: 'horizontal'
    Label:
        text: 'Total Figures: ' + str(root.counter)
    Label:
        id: _msg_label
        text: "Kivy started"

3 个答案:

答案 0 :(得分:2)

我怀疑正在发生的事情是在设置on_children()属性之前调用了DrawingSpace的{​​{1}}方法。由于每当status_bar的{​​{1}}更改时都会调用on_children()方法,因此可以通过添加对是否设置的检查来保护对children的引用:

DrawingSpace

关于为什么您的代码需要此代码,而书中的代码则不需要-我猜不到,因为我没有那本书。

答案 1 :(得分:1)

如您所见,在kv中定义属性会在事物依赖它们时引入解析顺序问题。最好的解决方案可能是只将动态创建的属性用于简单的事物,否则只需在类定义中正常定义属性即可。

答案 2 :(得分:1)

我正在读同一本书,并且遇到同样的问题!读取这些响应之后,@ JohnAnderson是正确的-DrawingSpace.on_children()status_bar存在于DrawingSpace中之前被调用。但为什么?

请记住,此处的应用程序的根小部件是ComicCreator中定义的comiccreator.kv。如果您查看那里的DrawingSpace实例,那么status_bar是在其自己的id之后定义的,因此似乎应该没有问题。

但是,我们必须记住,首先执行 kv类规则。因此,在查看drawingspace.kv时,我们看到<DrawingSpace>规则导入了python类(因此它已经知道了on_children方法),然后在其中添加了StickMan 规则。所有发生在DrawingSpace实例化之前的事件都会在status_bar文件中添加一个comiccreator.kv属性。

只需从StickMan中删除drawingspace.kv,错误将消失。 [编辑”中的以下内容有误:如果需要,您可以在StickMan中将DrawingSpace添加为comiccreator.kv的子级(在status_bar被添加),您将在视觉上获得相同的结果而不会出错。]

最后,您应该从@RelativeLayout中删除<DrawingSpace@RelativeLayout>。在python中定义自定义类并从基类继承后,您不再需要使用@运算符从kv中的基类继承。有关更多信息,请参见第10页的注释。