9

I wrote a code for a kind of android lock thing, whenever I try to get an specific ClickableImage using the id it raises the following error:

AttributeError: 'super' object has no attribute '__getattr__'

I've spent hours trying to look for a solution for this problem, I looked other people with the same issue, and people told them to change the site of the builder, because it needed to be called first to get the ids attribute or something like that, but everytime I move the builder, it raises the error "class not defined". Any clues?

Here is my code:

from kivy.app import App
from kivy.config import Config
from kivy.lang import Builder
from kivy.graphics import Line
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.behaviors import ButtonBehavior

#Variables
cords = ()
bld = Builder.load_file('conf.kv')

class Manager(ScreenManager): pass
class Principal(Screen): pass

class ClickableImage(ButtonBehavior, Image):
    def on_press(self):
        self.source = 'button_press.png'

    def on_release(self):
        self.source = 'button.png'
        self.ids.uno.source = 'button_press.png'

class canva(Widget):
    def on_touch_down(self, touch):
        global cords
        with self.canvas:
            touch.ud['line'] = Line(points=(touch.x, touch.y), width=1.5)
        cords = (touch.x, touch.y)

    def on_touch_move(self,touch):
        global cords
         touch.ud['line'].points = cords + (touch.x, touch.y)

    def on_touch_up(self,touch):
        self.canvas.clear()

class Api(App):    
    def build(self):
        return bld

if __name__ == '__main__':
    Api().run()

and here is my .kv file:

# conf to file:  test.py

<Manager>:
    Principal:

<Principal>:
    GridLayout:
        size_hint_x: 0.5
        size_hint_y: 0.6
        width: self.minimum_width
        cols: 3
        ClickableImage:
            id: 'uno'
            size: 10,10
            source: 'button.png'
            allow_strech: True
        ClickableImage:
            id: 'dos'
            size: 30,30
            source: 'button.png'
            allow_strech: True
    canva:
Peter Badida
  • 11,310
  • 10
  • 44
  • 90
gramsch
  • 379
  • 1
  • 5
  • 18

3 Answers3

9

Let's look at the output:

'super' object has no attribute '__getattr__'

In kv language id is set in a special way(up to 1.9.2 now), its value is not a string, because it's not a casual variable. You can't access it with <widget>.id.

I'd say it's similar to canvas, which is not a widget, yet it may look like that(which is why I was confused by your code :P). You've already noticed something: <some object> is like Python's something = <object> and that's (at least what I think) is the whole point of id's value not being a string(which to some is odd). If id was a string, there'd probably be needed a check to exclude it somehow from casual assigning values. Maybe it's because of performance or just simplicity.

Therefore let's say id is a keyword for a future keyword. In fact, it is, because the characters assigned to id will become a string key with a value of object got from WeakProxy, to the object WeakProxy points to. Or better said:

id: value

becomes

<some_root_widget>.ids[str(value)] = weakref.proxy(value)

where value becomes an object(what print(self) would return)

I suspect(not sure) that if you use string as the value for id, you'll end up with weakref / WeakProxy pointing to a string. I use the word point as it reminds me pointers, don't get confused with C pointers.

Now if you look again at the output:

  • super gives you an access to a class you inherit from

  • print('string id'.__getattr__) will give you the same error, but 'super' is substituted with the real value, because well... it doesn't have __getattr__

Therefore if you assign a string value to id, you'll get into this situation:

<some_root_widget>.ids[str('value')] = weakref.proxy('value')  # + little bit of magic

Although str('value') isn't necessarily wrong, by default you can't create weakref.proxy for a string. I'm not sure how Kivy handles this with WeakProxies, but if you assign a string to id, roughly this is what you get.

(Please correct me if I'm wrong)

Graham
  • 7,431
  • 18
  • 59
  • 84
Peter Badida
  • 11,310
  • 10
  • 44
  • 90
  • I managed to get the link to the object, calling it from the Principal class with `Principal().ids.uno` (I get to the 'uno' object) and it Returns `<__main__.ClickableImage object at 0x7f394cb428d8>`, I even can get the source of the image with `Principal().ids.uno.source` and it returns button.png (the source). But when I try to set the source to Button_press.png, it only changes locally (I can print the new value, but it doesn't change on the screen).. How could I change the value? (I'm trying to change a certain button when I press anywhere, not just in the button itself). Thank you :) – gramsch Aug 30 '16 at 19:21
  • 3
    @gramsch If you do it the way you wrote it in the comment (`Class().ids.something`), then you on each call create a new instance, not modifying the one you want. This topic is confusing from the beginning and if you come from non-OOP then the more confusing. Anyway, to explain it more: When you place `:` in kv, it's similar to `class Principal ...:`, but when you do `Principal:`(notice no brackets) then it creates an instance, that becomes a child of `Manager`. You need to access the instance you want to change a property in e.g. in "`dos`" button: `on_release: uno.source=` – Peter Badida Aug 30 '16 at 21:40
  • or in `ClickableImage` class: `self.parent.parent.ids.uno.source=` – Peter Badida Aug 30 '16 at 21:41
  • Nice! thank you so much!! I finally got it to work with the test. Just one last thing: when on the kv I call `Principal`, I create an instance of Principal() child of Manager(). How can I acces to that instance from outside? for example if I wanna call that from the `Canva` Class, how could I do it? at the end whats the name of the object. In other words, if in kv, `Principal:` Is like doing `x = Principal(Manager)`. What would be the actual name of `x`?? Thank you for helping :) – gramsch Sep 01 '16 at 02:19
  • @gramsch Actually, it's `Principal:`, but you're right. [This](http://stackoverflow.com/a/35996077/5994041) answer should help you with that problem. :) – Peter Badida Sep 01 '16 at 04:16
3

Don't use quotes while defining ids.

id: uno

instead of

id: 'uno'
Puru Soni
  • 41
  • 3
0

I found an unnecessary name:name in two parts of my code, after removing them the code worked properly

  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 02 '23 at 10:17