Let me preface this by saying that I know many people have asked this question. But after trying the following solutions:
Python/Kivy AttributeError: 'super' object has no attribute '__getattr__'
Kivy AttributeError: 'super' object has no attribute '__getattr__' with ScreenManager
python kivy AttributeError: 'super' object has no attribute '__getattr__'
I still haven't been able to rectify my issue, so I've come to the conclusion that I need help with my specific problem.
Context
I am building a larger GUI for school children (hence the mathematical id names), in which I create several custom widgets. As the program is already quite large, for the purpose of this question, I've fashioned a smaller one that is much more succinct. The intended output is as follows: on the screen are two texts "Blue" and "Green" separated by a line. When the user presses the up arrow, the "Blue" text becomes a red box, and when the user presses the down arrow, the "Green" text becomes a red box.
Code
Python file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import StringProperty, ListProperty
from kivy.graphics import Color, Rectangle
from kivy.core.window import Window
class ChildWidget(Widget):
text = StringProperty()
color = ListProperty()
def change_color_and_text(self):
self.text = 'Red'
with self.canvas:
self.canvas.clear()
Color(1, 0, 0, 1)
Rectangle(pos=(self.x, self.y), size=(self.width, self.height))
class ParentWidget(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.numerator = self.ids.numerator
self.denominator = self.ids.denominator
def on_key_down(self, window, key, *args):
if key == 38: # up
self.numerator.change_color_and_text()
elif key == 40: # down
self.denominator.change_color_and_text()
class ExampleApp(App):
def on_start(self):
parent = self.root.ids.parent
Window(on_key_down=parent.on_key_down)
if __name__ == '__main__':
ExampleApp().run()
Kivy file:
<ChildWidget>:
Label:
text: root.text
color: root.color
pos: self.x, self.y
<ParentWidget>:
canvas:
Color:
rgba: 0, 0, 0, 1
Line:
points: [self.x, self.y + self.height/2, self.x + self.width, self.y + self.height/2]
width: 1
ChildWidget:
id: numerator
text: 'Blue'
color: [0, 0, 1, 1]
pos: self.x, self.y + self.height + 5
size_hint: None, None
size: 50, 50
ChildWidget:
id: denominator
text: 'Green'
color: [0, 1, 0, 1]
pos: self.x, self.y - self.height - 5
size_hint: None, None
size: 50, 50
FloatLayout:
ParentWidget:
id: parent
pos: 300, 400
size_hint: None, None
size: 200, 200
Problem
When I run the above I get the following error:
File "kivy/properties.pyx", line 860, in kivy.properties.ObservableDict.__getattr__
KeyError: 'numerator'
During handling of the above exception, another exception occurred:
... [skip large chunk of immaterial traceback] ...
line 25, in __init__
self.numerator = self.ids.numerator
File "kivy/properties.pyx", line 863, in kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
Can anyone explain why I am getting this error?
Here are a few things I have tried adding geometry managers to my widgets and have tried rearranging the order in the python code, but those didn't work. I have also tried passing self.numerator = self.root.ids.numerator
but got an Attribute error: AttributeError: 'ParentWidget' object has no attribute 'root'
.
I just don't understand what is going wrong! The syntax looks correct, for example I haven't put quotes around my ids - which several other people asking this question did - and I think the id referencing is correct, as in the id numerator
is in the widget parent
so referencing self.ids.numerator
seems correct. But clearly I am going wrong somewhere.
Any help on the subject is greatly appreciated.
Thanks.
p.s. I understand that doing things the way I did them in the code above is probably not the best way to approach this. For example, I didn't need to create a custom widget that essentially is a label, as a label would have done everything I specified and would have reduced complexity in my code. Just remember that this code was created to emulate a larger Python script.
I think the ids
dict isn't yet populated at the point you try to access it.
Instead it's easiest to do this via kv, use numerator: numerator
in the ParentWidget
rule, and declare numerator = ObjectProperty()
in the Python code. Do the same for denominator.