0

I wanted to create a button module separate from my main file for my game. It runs okay in isolation. But the issue comes when I want to reference it in the main file as it either does not show on the screen at all (with no error message) or it will indefinitely call itself.

import pygame

class button:
    def init(self,  width, height, position = [], clr=[100, 100, 100], cngclr=None, func=None, text='', font_clr = (0, 0, 0), font="Courier New", font_size=16):

        width = int(width)
        height = int(height)
        size = (width, height)
        self.clr    = clr
        self.size   = size
        self.func   = func
        self.surf   = pygame.Surface(size)
        self.rect   = self.surf.get_rect(center=position)

        if cngclr:
            self.cngclr = cngclr
        else:
            self.cngclr = clr

        if len(clr) == 4:
            self.surf.set_alpha(clr[3])


        self.font = pygame.font.SysFont(font, font_size)
        self.txt = text
        self.font_clr = font_clr
        self.txt_surf = self.font.render(self.txt, 1, self.font_clr)
        self.txt_rect = self.txt_surf.get_rect(center=[wh//2 for wh in self.size])

    def draw(self, screen):
        self.mouseover()

        self.surf.fill(self.curclr)
        self.surf.blit(self.txt_surf, self.txt_rect)
        screen.blit(self.surf, self.rect)

    def mouseover(self):
        #what happens when the mouse hovers over the button
        self.curclr = self.clr
        pos = pygame.mouse.get_pos()
        #colour changes to cng_clr
        if self.rect.collidepoint(pos):
            self.curclr = self.cngclr

    def call_back(self, *args):
        if self.func:
            return self.func(*args)


class text:
    def __init__(self, msg, position, clr=[100, 100, 100], font="Courier New", font_size=15, mid=False):
        self.position = position
        self.font = pygame.font.SysFont(font, font_size)
        self.txt_surf = self.font.render(msg, 1, clr)

        if len(clr) == 4:
            self.txt_surf.set_alpha(clr[3])

        if mid:
            self.position = self.txt_surf.get_rect(center=position)


    def draw(self, screen):
        screen.blit(self.txt_surf, self.position)




# call back functions
def fn1():
    print('button1')
def fn2():
    print('button2')


if __name__ == '__main__':
    pygame.init()
    screen_size = (300, 200)
    size        = 10
    clr         = [255, 0, 255]
    bg          = (255, 255, 0)
    font_size   = 15
    font        = pygame.font.Font(None, font_size)
    clock       = pygame.time.Clock()

    screen    = pygame.display.set_mode(screen_size)
    screen.fill(bg)

    button1 = button(position=[80, 100], width = 100, height = 50, clr=(220, 220, 220),
                     cngclr=(255, 0, 0), func=fn1, text='button1', font_clr = (200, 10, 10))
    button2 = button((220, 100), (100, 50), (220, 220, 220), (255, 0, 0), fn2, 'button2', font_clr = (100, 100,100))

    button_list = [button1, button2]

    crash = True
    while crash:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                crash = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    crash = False

            elif event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    pos = pygame.mouse.get_pos()
                    for b in button_list:
                        if b.rect.collidepoint(pos):
                            b.call_back()

        

        pygame.display.update()
        clock.tick(60)

This is how I call it in the main file. With this code, the buttons do not even show on the screen.

        playButton = button(position = (60, 300), width = 100, height = 50, clr = blue, cng_clr = lightBlue, func = Game.startScreen(self, enteredUsername, cow), text ='Play', font_clr = orange)
        #this button allows the user to see account details and allows user to change
        #password, see their high score etc.
        accountButton = button(position = (180, 300), width = 250, height = 50, clr = blue, cng_clr = lightBlue, func = Game.accountDetails(self, enteredUsername), text = 'Account Details', font_clr = orange)
        #this creates a quit button that closes the game window only
        quitButton = button(position = (450, 300), width = 100, height = 50, clr = blue, cng_clr = lightBlue, func = Game.quitGame(self), text = 'Quit', font_clr = orange)
    
        buttonList = [playButton, accountButton, quitButton]

        crash = True
        while crash:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    crash = False
                elif event.type == pygame.KEYDOWN:
                    #if event.key == pygame.K_ESCAPE:
                    crash = False

                elif event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1:
                        pos = pygame.mouse.get_pos()
                        #this makes the function work
                        for b in buttonList:
                            if b.rect.collidepoint(pos):
                                b.call_back()
                                
            #draws all the buttons on a particular page
            for b in buttonList:
                b.draw(gameDisplay)
                pygame.display.update()

However, when I call the pause button. It causes the program to crash and says max recursion depth has been reached.

The code is this:

  global pauseButton
        pauseButton = button(position = (560, 10), width = 30, height = 30, clr = lightOrange, cng_clr = orange, func = Game.pause(self, enteredUsername),  text = "||")
       
        #this creates a cow object
        buttonList = [pauseButton,]
        self.mode = ""

        Game.setMode(self, enteredUsername)

        mode = self.mode
        
        global cow
        cow = player(225, 325, 150, 142)
        #cow.setSprite(self, 'blue')
        overPause = False
        global score
        score = 0
        run = True
        #sets the speed of animation
        speed = 180
        #creates an array to put the obstacles in
        #allows for random generation of obstacles       
        global obstacles
        obstacles = []
        #this sets the amount of time between each instance of an obstacle
        #the shorter amount of time the more obstacles generated
        cow.hit = False
        lowestSpeed = 2500
        highestSpeed = 3500
        pygame.time.set_timer(USEREVENT+2, random.randrange(lowestSpeed, highestSpeed))
        #the main loop
        while run:
            while cow.scoreAddition != 0:
                score += 100
                cow.scoreAddition = 0
            
            score += 1
            pygame.display.update()

            #this means the obstacles will appear in the three rows randomly             
            x = [140, 270, 395]
            randomNo = random.randint(0,2)
            redrawGameWindow(None,score, enteredUsername, None, mode)
            clock.tick(speed)
            #this allows for the scrolling background
            bgY -= 2.8 #1.4
            bgY2 -= 2.8 #1.4
            
            if bgY < bg.get_height() * -1:
                bgY = bg.get_height()  

            if bgY2 < bg.get_height() * -1:
                bgY2 = bg.get_height()

            event = pygame.event.poll()

            if event.type == USEREVENT + 2:
                #this makes it so the invincapple or milk is generated less than the dog
                #this makes the game harder
                r = random.randrange(0, 8)
                if r == 0:
                    obstacles.append(invincapple(x[randomNo], 30, 50, 57))

                elif (r >= 1 and r <= 6) or r == 8:
                    obstacles.append(dog(x[randomNo], 30, 70, 87))

                elif r == 7:
                    obstacles.append(milk(x[randomNo], 30, 60, 100))

            for obstacle in obstacles:
                obstacleType = type(obstacle)
                #this animates the obstacles
                obstacle.y += 1.4
                if obstacle.y < obstacle.height * -1:
                    obstacles.pop(obstacles.index(obstacle))
                if ((cow.y - 1 < obstacle.y + obstacle.height)and (obstacle.y + obstacle.height < 450)
                    and (obstacle.x > cow.x and obstacle.x + obstacle.width < cow.x + cow.width)):
                    cow.hit = True
                    obstacle.hit = True
                    redrawGameWindow(obstacle,score, enteredUsername, mode, obstacleType)

            keys = pygame.key.get_pressed()
            #this is the keyboard control
            if self.mode == "Standard":
                if keys[pygame.K_LEFT]:
                    #this checks if cow is not already in left
                    if not(cow.left):
                        cow.left = True
                        cow.right = False
                        
                        for obstacle in obstacles:
                            obstacleType = type(obstacle)
                            #this checks if the cow is in collision when the cow is in the left position
                            if (cow.y - 1 < obstacle.y + obstacle.height)and (obstacle.y + obstacle.height <450) and (obstacle.x > cow.x and obstacle.x + obstacle.width < cow.x + cow.width):
                                cow.hit = True
                                obstacle.hit = True
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                            else:
                                cow.hit = False
                                obstacle.hit = False
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                elif keys[pygame.K_RIGHT]:
                    #this checks if cow is not already in right
                    if not(cow.right):
                        cow.right = True
                        cow.left = False

                        for obstacle in obstacles:
                            obstacleType = type(obstacle)
                            #this checks if the cow is in collision when the cow is in the right position
                            if (cow.y - 1 < obstacle.y + obstacle.height) and (obstacle.y + obstacle.height <450) and (obstacle.x > cow.x and obstacle.x + obstacle.width < cow.x + cow.width):
                                cow.hit = True
                                obstacle.hit = True
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                            else:
                                cow.hit = False
                                obstacle.hit = False
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                elif keys[pygame.K_DOWN]:
                    #this places the cow back in the original centre position
                    cow.left = False
                    cow.right = False

                    for obstacle in obstacles:
                        obstacleType = type(obstacle)
                        #this checks if the cow is in collision when the cow is in the centre position
                        if (cow.y - 1 < obstacle.y + obstacle.height) and (obstacle.y + obstacle.height < 450)and (obstacle.x > cow.x and obstacle.x + obstacle.width < cow.x + cow.width):
                            cow.hit = True
                            obstacle.hit = True
                            redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                        else:
                            cow.hit = False
                            obstacle.hit = False
                            redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

            elif self.mode == "Hard":
                if keys[pygame.K_s]:
                    #this checks if cow is not already in left
                    if not(cow.left):
                        cow.left = True
                        cow.right = False
                        
                        for obstacle in obstacles:
                            obstacleType = type(obstacle)
                            #this checks if the cow is in collision when the cow is in the left position
                            if (cow.y - 1 < obstacle.y + obstacle.height)and (obstacle.y + obstacle.height <450) and (obstacle.x > cow.x and obstacle.x + obstacle.width < cow.x + cow.width):
                                cow.hit = True
                                obstacle.hit = True
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                            else:
                                cow.hit = False
                                obstacle.hit = False
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                elif keys[pygame.K_h]:
                    #this checks if cow is not already in right
                    if not(cow.right):
                        cow.right = True
                        cow.left = False

                        for obstacle in obstacles:
                            obstacleType = type(obstacle)
                            #this checks if the cow is in collision when the cow is in the right position
                            if (cow.y - 1 < obstacle.y + obstacle.height) and (obstacle.y + obstacle.height <450)and (obstacle.x > cow.x and obstacle.x + obstacle.width < cow.x + cow.width):
                                cow.hit = True
                                obstacle.hit = True
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                            else:
                                cow.hit = False
                                obstacle.hit = False
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                elif keys[pygame.K_c]:
                    #this places the cow back in the original centre position
                    cow.left = False
                    cow.right = False

                    for obstacle in obstacles:
                        obstacleType = type(obstacle)
                        #this checks if the cow is in collision when the cow is in the centre position
                        if (cow.y - 1 < obstacle.y + obstacle.height) and (obstacle.y + obstacle.height < 450)and (obstacle.x > cow.x and obstacle.x + obstacle.width < cow.x + cow.width):
                            cow.hit = True
                            obstacle.hit = True
                            redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                        else:
                            cow.hit = False
                            obstacle.hit = False
                            redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

            elif self.mode == "Beast":

                if keys[pygame.K_a]:
                    #this checks if cow is not already in left
                    if not(cow.left):
                        cow.left = True
                        cow.right = False
                        
                        for obstacle in obstacles:
                            obstacleType = type(obstacle)
                            #this checks if the cow is in collision when the cow is in the left position
                            if (cow.y - 1 < obstacle.y + obstacle.height)and (obstacle.y + obstacle.height <450)and (obstacle.x > cow.x and obstacle.x + obstacle.width < cow.x + cow.width):
                                cow.hit = True
                                obstacle.hit = True
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                            else:
                                cow.hit = False
                                obstacle.hit = False
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                elif keys[pygame.K_k]:
                    #this checks if cow is not already in right
                    if not(cow.right):
                        cow.right = True
                        cow.left = False

                        for obstacle in obstacles:
                            obstacleType = type(obstacle)
                            #this checks if the cow is in collision when the cow is in the right position
                            if (cow.y - 1 < obstacle.y + obstacle.height) and (obstacle.y + obstacle.height <450)and (obstacle.x > cow.x and obstacle.x + obstacle.width < cow.x + cow.width):
                                cow.hit = True
                                obstacle.hit = True
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                            else:
                                cow.hit = False
                                obstacle.hit = False
                                redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                elif keys[pygame.K_v]:
                    #this places the cow back in the original centre position
                    cow.left = False
                    cow.right = False

                    for obstacle in obstacles:
                        obstacleType = type(obstacle)
                        #this checks if the cow is in collision when the cow is in the centre position
                        if (cow.y - 1 < obstacle.y + obstacle.height) and (obstacle.y + obstacle.height < 450)and (obstacle.x > cow.x and obstacle.x + obstacle.width < cow.x + cow.width):
                            cow.hit = True
                            obstacle.hit = True
                            redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                        else:
                            cow.hit = False
                            obstacle.hit = False
                            redrawGameWindow(obstacle, score, enteredUsername, mode, obstacleType)

                
            if event.type == pygame.MOUSEMOTION:
                mx, my = pygame.mouse.get_pos()
                print("Mouse at (%d, %d)"%event.pos)
                if (mx >= 560 and mx <= 590) and (my >= 10 and my <= 40):
                        
                    pauseButton.color = lightOrange
                    pauseButton.redrawWindow(orange, blue, None)
                    pygame.display.update()
                    overPause = True
                    
                else:
                    pauseButton.color = orange
                    pauseButton.redrawWindow(orange, blue, None)

            elif event.type == pygame.MOUSEBUTTONUP and overPause == True:
                buttonSound = pygame.mixer.Sound('buttonClick.wav')
                pygame.mixer.Sound.play(buttonSound)
                time.sleep(0.3)
                Game.pause(self, enteredUsername)
                run = False
                    
            elif event.type == USEREVENT + 1:
                speed += 1
                    
            else:
                pygame.display.update()


            crash = True
            while crash:
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        crash = False
                    elif event.type == pygame.KEYDOWN:
                    #if event.key == pygame.K_ESCAPE:
                        crash = False

                    elif event.type == pygame.MOUSEBUTTONDOWN:
                        if event.button == 1:
                            pos = pygame.mouse.get_pos()
                        #this makes the function work
                        for b in buttonList:
                            if b.rect.collidepoint(pos):
                                b.call_back()
                                
                #draws all the buttons on a particular page
                for b in buttonList:
                    b.draw(gameDisplay)
                    pygame.display.update()

            gameFont = pygame.font.SysFont("Courier New", 20, bold = True)
            #this sets what the title will say
            global textSurface
            textSurface = gameFont.render("Score: " + str(score) , False, black)
            #puts the text on the window gameDisplay
            gameDisplay.blit(textSurface, (0, 0))
            clock.tick(speed)
furas
  • 134,197
  • 12
  • 106
  • 148
  • 2
    there is good rule to use `CamelCaseNames` for classes - ie. `class Button` - it helps to recognize class in code. See more [PEP 8 -- Style Guide for Python Code](https://www.pyhon.org/dev/peps/pep-0008/) – furas Jan 05 '21 at 17:34
  • you don't have to run ` pygame.display.update()` in `for`-loop. You shoudl run it only once i all `while run:` – furas Jan 05 '21 at 17:36
  • code is too long to say something. You can use `print()` in different places to see values in variable and which part of code is executed. Maybe your code never draw buttons so you don't see them. Or maybe you draw them in wrong place and after drawing buttons you clear screen and this way you can't see buttons. – furas Jan 05 '21 at 17:44
  • `func=` needs function name without `()` and arguments - ie, `func=Game.quitGame` - and later it should use `()` to execut it. If you use `func=Game.quitGame(self)` then you execute `Game.quitGame(self)` at once and you assing its result to `func=` - like `result = Game.quitGame(self)` and `...func=result`. And if want to assign function with arguments then use `lambda` to create function without argument `func=lambda:Game.quitGame(self)`. – furas Jan 05 '21 at 17:51
  • And this can makes your problem with recursion. It can run function assigned to button at once - and this can run code which run at once another function incorrectly assigned to button, which can run another function incorrectly assigned to button, etc. – furas Jan 05 '21 at 17:56

0 Answers0