Search code examples
pythonkivykivy-language

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


I'm trying to make a game in kivy and I'm facing an issue. Whenever I click on my "start game" button on my gamescreen it says

line 46, in move_ball
     ball = self.root.ids.ball
   File "kivy\properties.pyx", line 864, in kivy.properties.ObservableDict.__getattr__
 AttributeError: 'super' object has no attribute '__getattr__' 

My main.py

from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.image import Image
from kivy.core.audio import SoundLoader
from kivy.clock import Clock
from kivy.properties import NumericProperty

class HomeScreen(Screen):
    pass

    def play_sound(self):
        sound = SoundLoader.load('button press sound.wav.')
        if sound:
            sound.play()


    sound = SoundLoader.load('Crowd sound effect.wav')
    sound.loop = True
    sound.play()

class GameScreen(Screen):
    pass


class Ball(Image):
    velocity = NumericProperty(0)

    def on_touch_down(self, touch):
        self.source = "icons/ball1.png"
        self.velocity = 275
        super().on_touch_down(touch)

    def on_touch_up(self, touch):
        self.source = "icons/ball1.png"
        super().on_touch_up(touch)

class MainApp(App):
    GRAVITY = 300


    def move_ball(self, time_passed):
        ball = self.root.ids.ball
        ball.y = ball.y + ball.velocity * time_passed
        ball.velocity = ball.velocity - self.GRAVITY * time_passed


    def start_game(self):
        Clock.schedule_interval(self.move_ball, 1/60.)


    def change_screen(self, screen_name):
        # Get the screen manager from the kv file
        screen_manager = self.root.ids['screen_manager']
        screen_manager.current = screen_name


MainApp().run()

homescreen.kv

#:import utils kivy.utils
#:import FadeTransition kivy.uix.screenmanager.FadeTransition


<HomeScreen>:
FloatLayout:
    canvas:
        Color:
            rgb: utils.get_color_from_hex("#39B3F2")
        Rectangle:
            size: self.size
            pos: self.pos
    GridLayout:
        rows: 1
        pos_hint: {"top": 1, "left": 1}
        size_hint: 1, .9
        Image:
            source: "icons/keepyup.png"
    FloatLayout:
        Button:
            font_size: dp(20)
            font_name: 'SackersGothicStd-Medium.otf'
            text: "PLAY"
            color: "gold"
            pos_hint: { "center_x": .5, "center_y": .3}
            size: 80, 55
            size_hint: None, None
            background_normal: ''
            background_color: (57/255.0, 179/255.0, 242/255.0, .15)


            on_press:

            on_release:
                root.play_sound()
                root.manager.transition = FadeTransition()
                app.change_screen("game_screen")

gamescreen.kv

#:import utils kivy.utils


<GameScreen>:
FloatLayout:
    canvas:
        Color:
            rgb: utils.get_color_from_hex("#39B3F2")
        Rectangle:
            size: self.size
            pos: self.pos
    GridLayout:
        rows: 1
        pos_hint: {"top": 1, "left": 1}
        size_hint: 1, .1
        Image:
            source: "icons/sun.png"
    GridLayout:
        rows: 1
        pos_hint: {"top": 1, "left": 1}
        size_hint: 1, .2
        Image:
            source: "icons/clouds.png"
    GridLayout:
        rows: 1
        pos_hint: {"bottom": 1, "left": 1}
        size_hint: 1, .5
        Image:
            source: "icons/Field4.png"
            allow_stretch: True
            keep_ratio: False
            pos: self.pos

    Button:
        text: "Start game"
        size_hint: None, None
        on_release:
            app.start_game()

    Ball:
        source: "icons/ball1.png"
        size_hint: None, None
        size: 100, 100
        pos: 20, (root.height - 90) / 2.0
        id: ball

main.kv

#:include kv/homescreen.kv
#:include kv/gamescreen.kv


GridLayout:
cols: 1
ScreenManager:
    id: screen_manager
    HomeScreen:
        name: "home_screen"
        id: home_screen
    GameScreen:
        name: "game_screen"
        id: game_screen

I made another kivy project and tried the same thing without the screens and it doesn't show any errors! here's my code

My main.py

from kivy.app import App
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.properties import NumericProperty



class Ball(Image):
    velocity = NumericProperty(0)

    def on_touch_down(self, touch):
        self.source = "ball1.png"
        self.velocity = 275
        super().on_touch_down(touch)

    def on_touch_up(self, touch):
        self.source = "ball1.png"
        super().on_touch_up(touch)


class MainApp(App):
    GRAVITY = 300


    def move_ball(self, time_passed):
        ball = self.root.ids.ball
        ball.y = ball.y + ball.velocity * time_passed
        ball.velocity = ball.velocity - self.GRAVITY * time_passed


    def start_game(self):
        Clock.schedule_interval(self.move_ball, 1/60.)


MainApp().run()

main.kv

#:import utils kivy.utils

FloatLayout:
canvas:
    Color:
        rgb: utils.get_color_from_hex("#39B3F2")
    Rectangle:
        size: self.size
        pos: self.pos
GridLayout:
    rows: 1
    pos_hint: {"top": 1, "left": 1}
    size_hint: 1, .1
    Image:
        source: "sun.png"
GridLayout:
    rows: 1
    pos_hint: {"top": 1, "left": 1}
    size_hint: 1, .2
    Image:
        source: "clouds.png"
GridLayout:
    rows: 1
    pos_hint: {"bottom": 1, "left": 1}
    size_hint: 1, .5
    Image:
        source: "Field4.png"
        allow_stretch: True
        keep_ratio: False
        pos: self.pos

Button:
    text: "Start game"
    size_hint: None, None
    on_release:
        app.start_game()

Ball:
    source: "ball1.png"
    size_hint: None, None
    size: 100, 100
    pos: 20, (root.height - 90) / 2.0
    id: ball

What's wrong with my first project code? because my second one seems to work without getting any errors.


Solution

  • Although the traceback isn't fully shown in this question, this should be what it looks like at the end:

       File "C:\Users\weebi\Desktop\TestingZone\test1\kv\homescreen.kv", line 37, in <module>
         app.change_screen("game_screen")
       File "C:\Users\weebi\Desktop\TestingZone\test1\main.py", line 53, in change_screen
         screen_manager = self.root.ids.screen_manager
       File "kivy\properties.pyx", line 964, in kivy.properties.ObservableDict.__getattr__
     AttributeError: 'super' object has no attribute '__getattr__'
    

    The reason for the AttributeError: 'super' object has no attribute '__getattr__' exception usually has to do with a non-existence id in a .ids list. Here's a better version of your change_screen method that works as intended:

    def change_screen(self, screen_name):
        self.root.current = screen_name
    

    Also fixed your main.kv so that it switches between screens properly

    #:include kv/homescreen.kv
    #:include kv/gamescreen.kv
    
    
    ScreenManager:
        id: screen_manager
        HomeScreen:
            name: "home_screen"
            id: home_screen
        GameScreen:
            name: "game_screen"
            id: game_screen
    

    By the way, while this isn't necessarily related to the question, before trying your code, I noticed this line:

    ball = self.root.ids.ball
    

    I went ahead and checked the main.kv file and to my surprise, there's no root widget with the id of ball but there's one in the game_screen object, so my OCD acted up and I fixed it for you:

    def move_ball(self, time_passed):
        ball = self.root.ids.game_screen.ids.ball
        ball.y = ball.y + ball.velocity * time_passed
        ball.velocity = ball.velocity - self.GRAVITY * time_passed