Kivy: AttributeError со свойством, определенным в файле kv - PullRequest
1 голос
/ 20 октября 2019
  • Python : 3.6.8
  • Kivy : 1.9.0
  • PyCharm : 2019.2. 3

Я читаю « Kivy - интерактивные приложения и игры на Python Second Edition » во время написания и тестирования исходного кода в книге.

КогдаЯ заканчивал 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.

Я вставляю свой код ниже. Я опускаю эти файлы:

  • toolbox. *
  • comicwidgets. *
  • generaloptions. *

    , потому что я думаю, что онине актуальны, так что все проще. Но я буду вставлять их по требованию, если кто-нибудь спросит, или это не будет решено.

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

drawingpace.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

drawingpace.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 ]

2 голосов
/ 21 октября 2019

Подозреваю, что ваш on_children() метод DrawingSpace вызывается до того, как установлено свойство status_bar. Поскольку метод on_children() вызывается всякий раз, когда изменяется children из DrawingSpace, вы можете защитить ссылку на 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)

Почемуваш код нуждается в этом, а код в вашей книге - нет, я не могу догадаться, поскольку у меня нет этой книги.

1 голос
/ 23 октября 2019

Я следую за той же книгой, и у меня была та же проблема! Прочитав эти ответы, @JohnAnderson верен - DrawingSpace.on_children() вызывается до того, как status_bar существует в DrawingSpace. Но почему?

Помните, что корневым виджетом приложения здесь является ComicCreator, как определено в comiccreator.kv. Если вы посмотрите на экземпляр DrawingSpace там, status_bar определяется сразу после его собственного id, поэтому может показаться, что проблем не должно быть.

Но мы должны помнить, что правила kv выполняются первыми. Итак, глядя на drawingspace.kv, мы видим, что правило <DrawingSpace> импортирует класс python (поэтому он уже знает о методе on_children), а затем добавляет StickMan в правило . Все, что происходит перед тем, как * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *, добавлен в «* 1023» * атрибут status_bar.

Просто удалите StickMan из drawingspace.kv, и ошибка исчезнет. [ Редактировать следующее неверно: Если вы хотите, вы можете добавить StickMan как дочерний элемент DrawingSpace в comiccreator.kv (после добавления status_bar), и вы получите то же самоерезультат без ошибок.]

Наконец, вы должны удалить @RelativeLayout из <DrawingSpace@RelativeLayout>. Как только вы определили пользовательский класс в Python и наследуете от базового класса, вам больше не нужно наследовать от базового класса в kv с помощью оператора @. Для получения дополнительной информации см. Примечания на стр. 10.

1 голос
/ 20 октября 2019

Определение свойств в kv может привести к проблемам порядка разбора, когда от них что-то зависит, как вы обнаружили. Лучшее решение, вероятно, состоит в том, чтобы использовать динамически создаваемые свойства только для простых вещей, в противном случае просто определите свойства обычно в определении класса.

...