2

I'm trying to make a simple menu for a game in pygame, but I'm struggling to display interactive text for the menu buttons. I have a very simple menu, with the 'Start' and 'Quit' options, and I'm trying to display them using a Button class I made.

For some reason the button background blits but the text doesn't appear. I get a bunch of rectangles which change colour correctly as I scroll over them but no text.

I looked at similar questions but can't seem to figure out why mine doesn't work. Any help or guidance would be appreciated.

Here is a code sample below. Colours are defined in a separate python file.

class Start(SceneBase):
    def __init__(self):
        SceneBase.__init__(self)

        self.options = ['Start', 'Quit']
        self.buttons = pygame.sprite.Group()

    def initGraphics(self, screen):
        SceneBase.initGraphics(self, screen)

        info = pygame.display.Info()
        screenWidth, screenHeight = info.current_w, info.current_h

        font = pygame.font.Font('freesansbold.ttf', 30)

        for i, option in enumerate(self.options):
            rect = pygame.Rect(int(screenWidth/2) - 50, int(screenHeight/2) - 100 + i*50, 100, 30)
            passive_color = colors.BLACK
            active_color = colors.RED

            button = Button(rect, font, active_color, option, colors.WHITE, passive_color, option, colors.WHITE)

            self.buttons.add(button)

    def ProcessInput(self, events, pressed_keys):
        pass

    def Update(self):
        self.buttons.update()

    def Render(self):
        self.screen.fill(colors.WHITE)
        self.buttons.draw(self.screen)
        pygame.display.flip()


class Button(pygame.sprite.Sprite):
    def __init__(self, rect, font, active_color, active_text, active_textcolor, passive_color, passive_text, passive_textcolor):
        # Call the parent class (Sprite) constructor
        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.Surface((rect[2], rect[3]))

        self.rect = rect

        self.font = font

        self.active_color = active_color
        self.active_text = active_text
        self.active_textcolor = active_textcolor
        self.passive_color = passive_color
        self.passive_text = passive_text
        self.passive_textcolor = passive_textcolor

    def update(self):
        mouse = pygame.mouse.get_pos()

        if self.rect.x <= mouse[0] <= self.rect.x + self.rect.w and self.rect.y <= mouse[1] <= self.rect.y + self.rect.h:
            self.image.fill(self.active_color)
            self.renderButtonText(self.active_text, self.active_textcolor)
        else:
            self.image.fill(self.passive_color)
            self.renderButtonText(self.passive_text, self.passive_textcolor)

    def renderButtonText(self, text, color):
        textsurf = self.font.render(text, True, color)
        textrect = textsurf.get_rect()
        textrect.center = self.rect.center
        self.image.blit(textsurf, textrect)

This is the skeleton code for the SceneBase class:

pygame.init()


def run_game(width, height, fps, starting_scene):
    screen = pygame.display.set_mode((width, height))
    clock = pygame.time.Clock()

    active_scene = starting_scene

    initialized = False

    while active_scene:

        if not initialized:
            active_scene.initGraphics(screen)
            initialized = True

        pressed_keys = pygame.key.get_pressed()

        # Event filtering
        filtered_events = []
        for event in pygame.event.get():
            quit_attempt = False
            if event.type == pygame.QUIT:
                quit_attempt = True
            elif event.type == pygame.KEYDOWN:
                alt_pressed = pressed_keys[pygame.K_LALT] or \
                              pressed_keys[pygame.K_RALT]
                if event.key == pygame.K_ESCAPE:
                    quit_attempt = True
                elif event.key == pygame.K_F4 and alt_pressed:
                    quit_attempt = True

            if quit_attempt:
                active_scene.Terminate()
            else:
                filtered_events.append(event)

        active_scene.ProcessInput(filtered_events, pressed_keys)
        active_scene.Update()
        active_scene.Render()

        active_scene = active_scene.next

        pygame.display.flip()
        clock.tick(fps)

#==============================================================================
# The rest is code where you implement your game using the Scenes model


run_game(500, 500, 60, Start())
2
  • 1
    Could you explain what exactly is going wrong? It helps people look for bugs since they know what to look for. Commented Aug 14, 2017 at 4:03
  • Ok, I forgot about that. Thanks for pointing that out. Commented Aug 14, 2017 at 15:10

1 Answer 1

1

It seems that you're rendering the button text before you're rendering the background, so what is probably happening is that the text is being drawn, but the button is being drawn over it in the same frame, so it seems that there's no text. Try adding the update function's code to a new function called checkIfHovering, and have it write:

    def checkIfHovering(self):
        mouse = pygame.mouse.get_pos()

        if self.rect.x <= mouse[0] <= self.rect.x + self.rect.w and self.rect.y <= mouse[1] <= self.rect.y + self.rect.h:
            self.image.fill(self.active_color)
            self.renderButtonText(self.active_text, self.active_textcolor)
        else:
            self.image.fill(self.passive_color)
            self.renderButtonText(self.passive_text, self.passive_textcolor)

and in addition to that, change the Render function to have:

    def Render(self):
        self.screen.fill(colors.WHITE)
        self.buttons.draw(self.screen)
        self.buttons.checkIfHovering()
        pygame.display.flip()

That way it's printing the text after the button backgrounds are being drawn.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your response! I actually managed to find a solution on my own in May 2020. The bug had to do with drawing the labels at a weird position that was out of the visibility. The suspect line textrect.center = self.rect.center from Button#renderButtonText() set the relative coordinates of the button's center position to the absolute coordinates of the screen's center position. Once I fixed the coordinates appropriately, the button text was visible. It was an opaque bug that I didn't catch until I played around with values. Thanks anyway!
^ Mind you, this is what I'm interpreting the solution as 1 year later...but that line is the main thing that seemed to change when I look through my commit history. So it might actually not be the source of the error, but it worked as far as I could tell.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.