AttributeError: у объекта 'super' нет атрибута '__getattr__' Ошибка при использовании BoxLayout с несколькими kv-файлами в Kivy - PullRequest
3 голосов
/ 16 марта 2019

Как мне хорошо известно, этот вопрос уже задавался несколько раз. Но попробовав следующие решения:

Я пришел к выводу, что мне нужна помощь в решении моей конкретной проблемы. Перечисленные решения не работают в моем конкретном случае.

Следующая ситуация:

В настоящее время я пытаюсь разработать приложение для смартфонов, используя kivy. Поскольку мне нравится мой код, довольно чистый и ясный, я разделил код на Kivy на несколько kv-файлов. Предполагается, что код Python должен иметь в первую очередь логику и ничего более. Чтобы заставить его работать должным образом, мне нужно сослаться на экземпляры разных объектов в разных kv-файлах. Чтобы прояснить мою проблему, я построил довольно простой пример:

ФАЙЛ: try.py

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.factory import Factory
from kivy.uix.label import Label
from kivy.lang import Builder

x= 1

class ComplexBox(Widget):
    def testit(self):
        self.ids.layout.add_widget(Label(text = "Requirement A met."))
    def addsome(self):
        global x
        self.ids.layout.add_widget(SomeWidget(id="XXX"+str(x)))
        x = x +1
    pass

class SomeWidget(Widget):
    def change(self):
        self.ids.REQB.text = "Requirement B met."
    pass

class RequirementC(Widget):
    def triggerC(self):
        self.ids.ERRORBUTTON.text = "Requirement C met"
    pass

class Attempt(App):
    def build(self):
        return presentation
    pass


presentation = Builder.load_file("attempt.kv")
Attempt().run()

ФАЙЛ: try.kv

#:kivy 1.0
#:include attemptsupp.kv
#:include attemptsuppC.kv

# root
<ComplexBox>:
    BoxLayout:
        id: layout
        size: root.size
        Button:
            id: ERRORBUTTON
            text: "add"
            on_press: root.addsome()
            on_release: root.testit()
BoxLayout:
    orientation: 'vertical'
    ComplexBox:
    RequirementC:

ФАЙЛ: попытки запуска.кв

#:kivy 1.0

# rules for the widget
<SomeWidget>:
    BoxLayout:
        pos: root.pos
        size: root.size
        orientation: "vertical"
        Label:
            id: REQB
            text: "hello"
        Button:
            text: "world"
            on_release: root.change()

ФАЙЛ: попытки открытия файла.kv

#:kivy 1.0

<RequirementC>:
    Button:
        id: REQC
        text: "Press"
        on_release: root.triggerC()

изображение запущенной программы - нажмите кнопку «Нажать» - чтобы получить ошибку

При запуске с kivy версии 1.10 и Python версии 3.7.2 программа сначала запускается идеально. Но когда я нажимаю на кнопку с надписью ERRORBUTTON с надписью «press», я получаю эту ошибку:

...--default --nodebug --client --host localhost --port 57777...\attempt.py "
[INFO   ] [Logger      ] Record log in...\.kivy\logs\kivy_19-03-15_31.txt
[INFO   ] [Kivy        ] v1.10.1
[INFO   ] [Python      ] v3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 
...
[INFO   ] [Window      ] auto add sdl2 input provider
[INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked
[WARNING] [Lang        ] attemptsupp.kv has already been included!
[WARNING] [Lang        ] attemptsuppC.kv has already been included!
[INFO   ] [Base        ] Start application main loop
[INFO   ] [GL          ] NPOT texture support is available
[INFO   ] [Base        ] Leaving application in progress...
 Traceback (most recent call last):
   File "kivy\properties.pyx", line 838, in kivy.properties.ObservableDict.__getattr__
 KeyError: 'ERRORBUTTON'

 During handling of the above exception, another exception occurred:

 Traceback (most recent call last):
   File "...\ptvsd_launcher.py", line 45, in <module>
     main(ptvsdArgs)
   ...
   File "e:\Daten\Github_Projects\pc-clicker\attempt.py", line 35, in <module>
     Attempt().run()
   File "...\lib\site-packages\kivy\app.py", line 826, in run
     runTouchApp()
...
   File ...\lib\site-packages\kivy\lang\builder.py", line 64, in custom_callback
     exec(__kvlang__.co_value, idmap)
   File ...\attemptsuppC.kv", line 7, in <module>
     on_release: root.triggerC()
   File "...\attempt.py", line 25, in triggerC
     self.ids.ERRORBUTTON.text = "Requirement C met"
   File "kivy\properties.pyx", line 841, in kivy.properties.ObservableDict.__getattr__
 AttributeError: 'super' object has no attribute '__getattr__'

Даже если я сократил сообщение об ошибке, должно быть ясно, что происходит. ОШИБКА ID, на которую я ссылаюсь в классе RequirementC, не найдена в словаре. Теперь на мой вопрос:

Как я могу заставить это работать? Что мне не хватает?

Вот вкратце несколько вещей, которые я попробовал:

  • Я попытался обернуть BoxLayouts в Screen и получить к ним доступ через менеджер экрана.
  • Я попытался изменить порядок в коде Python. (например, сначала загрузка основного файла kv)
  • Я попытался использовать Фабрику Строителей и зарегистрировать там разные Классы.
  • Я попытался изменить ссылки. (Например, self.ids. ['ERRORBUTTON'] ...)

Ни одна из этих попыток, похоже, не сработала в моем случае.

Итак, подведем итог:

Как я могу заставить мои kivy References из разных классов работать должным образом и почему идентификатор ERRORBUTTON не входит в диктовку, которую я изучаю?

Ответы [ 2 ]

1 голос
/ 16 марта 2019

Проблема вызвана распространенной ошибкой, идентификаторы относятся к виджету, например, в вашем случае давайте проанализируем выражение:

self.ids.ERRORBUTTON

Кто является собой? selfэто экземпляр RequirementC.

какой у вас экземпляр? , тогда давайте посмотрим .kv, где реализован RequirementC:

<RequirementC>:
    Button:
        id: REQC
        text: "Press"
        on_release: root.triggerC()

Если вы заметили единственный идентификаторкоторый имеет доступ к REQC, поэтому идентификатор ERRORBUTTON не существует для RequirementC.

Так к какому классу принадлежит идентификатор ERRORBUTTON? Итак, давайте рассмотрим, где был создан ERRORBUTTON:

 # ...

<ComplexBox>:
    BoxLayout:
        id: layout
        size: root.size
        Button:
            id: ERRORBUTTON
            text: "add"
            on_press: root.addsome()
            on_release: root.testit()
 # ...

Как видите, ERRORBUTTON - это идентификатор ComplexBox.


С учетом того, что было упомянуто в предыдущей части, мы уже знаем причину проблемы.Прежде чем дать решение, мы сначала разберемся с базовым принципом программирования: класс - это абстракция поведения, он должен четко определить, что вы хотите показывать извне (поэтому, если вы проверяете документы какой-либо библиотеки, не документируйте все методыили все классы, так как идея состоит в том, чтобы абстрагировать класс, то есть тот, кто использует эту библиотеку, не хочет знать, как она работает внутри с такой точностью), поэтому хорошо бы спроектировать мышление, какие методы будут иметь классы,Например, предположим, что мы создаем класс Person, у этого класса есть определенные атрибуты, такие как размер или вес, что, если вы считаете необходимым выявить, сколько весит ваше сердце или мозг?Ну нет.То же самое происходит в вашем случае.

Решение состоит в том, чтобы выставить событие on_release так, чтобы оно было частью класса RequirementC, в дополнение к представлению ERRORBUTTON в качестве свойства (в моем случае я не хотел бы использоватьидентификаторы, поскольку они делают код менее читабельным), а затем устанавливают соединение в месте с общей областью действия.

*. py

# ...

class RequirementC(Widget):
    def __init__(self, **kwargs):
        self.register_event_type('on_release')
        super().__init__(**kwargs)

    def on_release(self):
        pass

# ...

попытка.кв

#:kivy 1.0
#:include attemptsupp.kv
#:include attemptsuppC.kv

# root
<ComplexBox>:
    error_button: ERRORBUTTON # <---
    BoxLayout:
        id: layout
        size: root.size
        Button:
            id: ERRORBUTTON
            text: "add"
            on_press: root.addsome()
            on_release: root.testit()
BoxLayout:
    orientation: 'vertical'
    ComplexBox:
        id: complex_box
    RequirementC:
        on_release: complex_box.error_button.text = "Requirement C met"

попыткиuppC.kv

#:kivy 1.0

<RequirementC>:
    Button:
        id: REQC
        text: "Press"
        on_release: root.dispatch('on_release')
0 голосов
/ 19 марта 2019

Итак, после небольшого исследования я нашел ответ, который искал. Для тех, кто также может споткнуться о мою проблему, вот оно:

class RequirementC(Widget):
def triggerC(self):
    presentation.children[1].ids.ERRORBUTTON.text = "Requirement C met"
pass

Переходя к дочерним элементам презентации, можно использовать метод «идентификаторы».

Но для тех, кто испытывает желание использовать его сейчас, я бы рекомендовал перебирать детей, чтобы найти правильный идентификатор, вместо того, чтобы давать «жесткую» ссылку (children [1]).

...