簡體   English   中英

Kivy:在 kv 文件中定義屬性的 AttributeError

[英]Kivy: AttributeError with property defined in kv file

  • 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 之間的關系有任何區別。

我在下面粘貼我的代碼。 我省略了這些文件:

  • 工具箱。*

  • 漫畫小部件。*

  • 常規選項。*

    因為我認為它們不相關,所以一切都更容易。 但是,如果有人問或這沒有得到解決,我會按需粘貼它們。

漫畫創作者.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()

漫畫創作者.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

繪圖空間.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

繪圖空間.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:

狀態欄.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

狀態欄.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屬性之前調用了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)

至於為什么你的代碼需要這個,而你書中的代碼不需要——我猜不出來,因為我沒有那本書。

正如您所發現的,在 kv 中定義屬性可能會在事情依賴於它們時引入解析順序問題。 最好的解決方案可能是僅將動態創建的屬性用於簡單的事情,否則只需在 class 定義中正常定義屬性。

我正在關注同一本書並且遇到了同樣的問題,在閱讀了這些回復之后,@JohnAnderson 是正確的 - DrawingSpace.on_children()status_bar存在於DrawingSpace之前被調用。 但為什么?

請記住,此處應用程序的根小部件是一個ComicCreator ,定義在comiccreator.kv中。 如果你看一下那里的DrawingSpace實例, status_bar是在它自己的id之后定義的,所以看起來應該沒有問題。

但是,我們必須記住,首先執行的是kv class 規則 所以查看drawingspace.kv ,我們看到<DrawingSpace>規則導入了 python class (所以它已經知道on_children方法),然后在規則中添加了一個StickMan DrawingSpace的 INSTANCE 之前發生的所有事情都會在comiccreator.kv文件中添加一個status_bar屬性。

只需從drawingspace.kv中刪除StickMan ,錯誤就會消失。 [編輯以下不正確:如果需要,您可以在 Comiccreator.kv 中添加StickMan作為DrawingSpace的子項(在status_bar comiccreator.kv之后),您將在視覺上得到相同的結果而沒有錯誤。]

最后,您應該從<DrawingSpace@RelativeLayout>中刪除@RelativeLayout 在 python 中定義自定義 class 並從基礎 class 繼承后,您不再需要使用 kvv2 中的基礎@運算符繼承。 有關這方面的更多信息,請參見第 10 頁的注釋。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM