问题
As I am very well aware, this question has been asked several times already. But after trying the following solutions:
- Python - Kivy: AttributeError: 'super' object has no attribute '__getattr__' when trying to get self.ids
- Python/Kivy AttributeError: 'super' object has no attribute '__getattr__'
- How can i access object properties of another class in kivy without getting AttributeError: 'super' object has no attribute '__getattr__'
- AttributeError: 'super' object has no attribute '__getattr__'
I have reached the conclusion that i need help in my specific problem. The solutions listed didn't seem to work in my specific case.
Following situation:
I am currently trying to develop an application for smartphones using kivy. Since i like my code pretty clean and clear structured i have split my Kivy code into several kv-files. The python code is supposed to have primarily the logic and nothing more. In order to get it working properly i need to reference the instances of the different objects in the different kv-files. In order to make my problem clear i have constructed a fairly simple example:
FILE: attempt.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()
FILE: attempt.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:
FILE: attemptsupp.kv
#: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()
FILE: attemptsuppC.kv
#:kivy 1.0
<RequirementC>:
Button:
id: REQC
text: "Press"
on_release: root.triggerC()
picture of the Running Program - press the "Press"- button to get the error
Running with kivy version 1.10 and Python version 3.7.2 the program first starts perfectly fine. But when I press on the Button labeled "press" with the id ERRORBUTTON I get this error:
...--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__'
Even though I shortened the error message it should be clear what happens. The ERRORBUTTON id I am referencing in the RequirementC Class can't be found in the dictionary. Now to my question:
How can i make it work? What am I a missing?
Here in short a few things i have tried:
- I have tried wrapping the BoxLayouts in Screen and accessing them over the screenmanager.
- I have tried rearranging the order in the python code. (for example the loading of the main kv file first)
- I have tried using the Builder Factory and registering the different Classes there.
- I have tried changing the references. (For example self.ids.['ERRORBUTTON']...)
None of these attempts seem to have worked in my case.
So to sum it all up:
How can I get my kivy References across different classes to work properly and why is the ERRORBUTTON id not in the dict I am looking into?
回答1:
The problem is caused by a common error, the ids are relative to a widget, for example in your case let's analyze the expression:
self.ids.ERRORBUTTON
Who is self? self is the instance of RequirementC.
what instance do you have? then let's see the .kv where RequirementC is implemented:
<RequirementC>:
Button:
id: REQC
text: "Press"
on_release: root.triggerC()
If you notice the only id that has access is to REQC, so the id ERRORBUTTON does not exist for RequirementC.
So which class does the id ERRORBUTTON belong to? So let's review where ERRORBUTTON was created:
# ...
<ComplexBox>:
BoxLayout:
id: layout
size: root.size
Button:
id: ERRORBUTTON
text: "add"
on_press: root.addsome()
on_release: root.testit()
# ...
As you can see ERRORBUTTON is a id of ComplexBox.
With what was mentioned in the previous part, we already know the cause of the problem. Before giving a solution we first understand a basic principle of programming: A class is an abstraction of a behavior, it must clearly define that you want to expose to the outside (therefore if you check the docs of any library do not document all the methods or all the classes since the idea is to abstract the class, that is to say, that whoever uses that library does not want to know how it works internally with such precision), so it is good to design thinking what methods the classes will have. For example let's say that we create a Person class, this class has certain attributes such as size or weight, what if you think it is necessary to expose how much your heart or brain weighs? Well, no. The same happens in your case.
The solution is to expose the event on_release so that it is part of the RequirementC class, in addition to expose ERRORBUTTON as a property (in my case I do not like to use the ids since they make the code less readable) and then make the connection in a place with common scope.
*.py
# ...
class RequirementC(Widget):
def __init__(self, **kwargs):
self.register_event_type('on_release')
super().__init__(**kwargs)
def on_release(self):
pass
# ...
attempt.kv
#: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"
attemptsuppC.kv
#:kivy 1.0
<RequirementC>:
Button:
id: REQC
text: "Press"
on_release: root.dispatch('on_release')
回答2:
So after a bit of research i found the answer i was looking for. For those who might also stumble over my problem here it comes:
class RequirementC(Widget):
def triggerC(self):
presentation.children[1].ids.ERRORBUTTON.text = "Requirement C met"
pass
By going over the children of the presentation it is possible to use the "ids" method.
But for those tempted to use it now i would recommend iterating over the children to find the correct id instead of giving a "hard" reference (children[1]).
来源:https://stackoverflow.com/questions/55191901/attributeerror-super-object-has-no-attribute-getattr-error-when-using-b