【问题标题】:Kivy: AttributeError with property defined in kv fileKivy:在 kv 文件中定义属性的 AttributeError
【发布时间】:2019-10-20 01:25:08
【问题描述】:
  • Python:3.6.8
  • 基维: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"

【问题讨论】:

  • 我尚未对此进行测试,但我会尝试将 status_bar = ObjectProperty(None) 添加到您的 DrawingSpace 类定义中。
  • @JohnAnderson 抱歉这么晚才回复。这样做会抛出AttributeError: 'NoneType' object has no attribute 'counter'
  • 尝试用if self.status_bar is not None: 保护on_children 中的那一行
  • ¡成功了!但是,为什么下载的源代码不需要呢?
  • 您可以发布您的解决方案,我会接受它,但知道为什么会发生此错误会很好(也适用于阅读本文的人)。我还不明白为什么 status_bar 一开始就不存在

标签: python python-3.x kivy attributeerror


【解决方案1】:

我怀疑发生的事情是在设置 status_bar 属性之前调用了 DrawingSpaceon_children() 方法。由于每当DrawingSpacechildren 发生变化时都会调用on_children() 方法,因此您可以通过添加检查是否已设置来保护对status_bar 的引用:

class DrawingSpace(RelativeLayout):
    def on_children(self, instance, value):
        if self.status_bar is not None:
            self.status_bar.counter = len(self.children)

为什么你的代码需要这个,而你书中的代码不需要——我猜不出来,因为我没有那本书。

【讨论】:

    【解决方案2】:

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

    请记住,此处应用的根小部件是ComicCreator,如comiccreator.kv 中所定义。如果你看一下DrawingSpace 的实例,status_bar 是在它自己的id 之后定义的,所以看起来应该没有问题。

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

    只需从drawingspace.kv 中删除StickMan,错误就会消失。 [编辑以下不正确:如果需要,您可以在comiccreator.kv 中添加StickMan 作为DrawingSpace 的子项(在添加status_bar 之后),您将直观地得到结果相同,没有错误。]

    最后,您应该从&lt;DrawingSpace@RelativeLayout&gt; 中删除@RelativeLayout。一旦在 python 中定义了自定义类并从基类继承,您就不再需要使用@ 运算符从 kv 中的基类继承。有关详细信息,请参阅第 10 页的注释。

    【讨论】:

    • 我今天很忙检查这个,但我保证我会尽快阅读并尝试并告诉你。
    • 我真的很抱歉这么长时间的延误。个人情况。看来你是对的。评论 StickMan 确实解决了这个问题,但将其添加到 Comiccreator.kv 中,在 DrawingSpace 下无法打印 StickMan。我想解决这个问题,以便更改最佳答案并将您的答案标记为最佳。
    • 这是由同一问题引起的。您将在drawingspace.kv 中添加StickMan,即在&lt;DrawingSpace&gt; 规则 中。您需要在comiccreator.kv 中的&lt;ComicCreator&gt; 规则(如上所述)中将其添加到DrawingSpace instance 下。当 DS 规则执行时,DrawingSpace 只是一个RelativeLayout,然后你添加StickMan 触发on_children() 方法.. 但它不知道status_bar 是什么!它的status_bar 属性尚未创建,因为它是在instance 中创建的(在comiccreator.kv 中,在&lt;ComicCreator&gt; 规则中)。
    • StickMan 应该在comiccreator.kv 中的DrawingSpaceheight 属性下,在&lt;ComicCreator&gt; 规则中。代码没有错,所以你应该保持原样。当您构建自己的应用程序时,重要的是要了解执行顺序在这里发生了什么......我不建议将不需要的检查放在适当的位置 - 从长远来看只会让您更加困惑。您可能想阅读@JohnAnderson 帮助我的this similar question
    • 看来您正确更新了comiccreator.kv。在drawingspace.kv 中,确保删除StickMan。同时从&lt;DrawingSpace@RelativeLayout&gt; 中删除@RelativeLayout。如果仍然产生错误,请使用其他文件更新您的帖子,我会尝试找出问题所在。
    【解决方案3】:

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

    【讨论】:

    • 抱歉这么晚才回复。我仍在阅读这本书,我不知道如何正确定义该属性。此外,书中的源代码似乎也没有在 kv 文件之外做任何事情。
    猜你喜欢
    • 2018-11-04
    • 1970-01-01
    • 2018-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多