PyGame Text Under The Button

Viewed 128

I have an problem with my code, specifically with pygame and buttons

Initially, i was adding buttons manually, but i decided that it would be better to use classes instead. However, the text appears underneath my button instead of on top of it.

How do I get the text to appear ontop of the button?

Here is My Code:

import pygame as P
import sys

P.init()

class MENU():
    def __init__(self, BUTTON_COUNT, WINDOW, RESOLUTION, BUTTON_WIDTH, BUTTON_HEIGHT, BUTTON_PADDING_X, BUTTON_PADDING_Y):
        self.WINDOW = WINDOW
        self.BUTTON_COUNT = BUTTON_COUNT
        self.RESOLUTION = RESOLUTION
        self.BUTTON_WIDTH = BUTTON_WIDTH
        self.BUTTON_HEIGHT = BUTTON_HEIGHT
        self.BUTTON_PADDING_X = BUTTON_WIDTH + BUTTON_PADDING_X
        self.BUTTON_PADDING_Y = BUTTON_HEIGHT + BUTTON_PADDING_Y

        self.SETTINGS_ON = True
        self.ONCE = False
        self.FPS = P.time.Clock()

        while self.SETTINGS_ON:
            self.FPS.tick(30)
            self.WINDOW.fill((0, 0, 0))

            self.CENTER_X = RESOLUTION[0]//2
            self.CENTER_Y = RESOLUTION[1]//2

            self.TITLE_FONT = P.font.SysFont("calibri", 75)
            self.MESSAGE_FONT = P.font.SysFont("calibri", 20)
            self.BUTTON_FONT = P.font.SysFont("calibri", 25)

            if self.ONCE == False:
                self.CREATE_BUTTONS((127, 127, 127), (191, 191, 191), ("BUTTON 1", "BUTTON 2", "BUTTON 3"))
                self.ONCE = True
            if self.ONCE == True:
                self.VIEW_BUTTONS()


            event = P.event.wait()
            if event.type == P.KEYDOWN and event.key == P.K_RETURN:
                self.SETTINGS_ON = False

            if event.type == P.MOUSEMOTION:
                for i in range(0, self.BUTTON_COUNT):
                    if vars(self)["BTN"+str(i+1)+"_RECT"].collidepoint(event.pos):
                        vars(self)["BTN"+str(i+1)+"_CX"] = vars(self)["BTN"+str(i+1)+"_C1"]
                    else:
                        vars(self)["BTN"+str(i+1)+"_CX"] = vars(self)["BTN"+str(i+1)+"_C2"]

            if event.type == P.MOUSEBUTTONDOWN:
                for i in range(0, self.BUTTON_COUNT):
                    if vars(self)["BTN"+str(i+1)+"_RECT"].collidepoint(event.pos):
                        print("BUTTON CLICK")

            P.display.update()

    def CENTER_IT(self, text):
        self.CENTERED = (self.CENTER_X-text.get_width()//2, self.CENTER_Y-text.get_height()//2)

    def CREATE_BUTTONS(self, COLOR1, COLOR2, TEXTS):
        for i in range(0, self.BUTTON_COUNT):
            vars(self)["BTN"+str(i+1)+"_CX"] = COLOR1
            vars(self)["BTN"+str(i+1)+"_C1"] = COLOR1
            vars(self)["BTN"+str(i+1)+"_C2"] = COLOR2
            vars(self)["BTN"+str(i+1)+"_RECT"] = P.Rect(self.CENTER_X-self.BUTTON_WIDTH//2, self.CENTER_Y-self.BUTTON_HEIGHT//2-self.BUTTON_PADDING_Y*(i-1), self.BUTTON_WIDTH, self.BUTTON_HEIGHT)
            vars(self)["BTN"+str(i+1)+"_TEXT"] = self.BUTTON_FONT.render(TEXTS[i], True, (255, 255, 255))
    def VIEW_BUTTONS(self):
        for i in range(0, self.BUTTON_COUNT):
            P.draw.rect(self.WINDOW, vars(self)["BTN"+str(i+1)+"_CX"], vars(self)["BTN"+str(i+1)+"_RECT"])
            self.CENTER_IT(vars(self)["BTN"+str(i+1)+"_TEXT"])
            self.WINDOW.blit(vars(self)["BTN"+str(i+1)+"_TEXT"], (self.CENTERED[0], self.CENTERED[1]+self.BUTTON_PADDING_Y*(i-1)))


class MainWindow():
    def __init__(self):
        self.RESOLUTION = (1024, 768)
        self.WINDOW = P.display.set_mode(self.RESOLUTION)
        self.Variables()
        self.Main()

    def Variables(self):
        self.GAMEMODE = -1
        self.ONCE = False
        self.FPS = P.time.Clock()

    def Main(self):
        while True:
            self.FPS.tick(30)
            if self.GAMEMODE == -1:
                if self.ONCE == False:
                    SETTINGS = MENU(3, self.WINDOW, self.RESOLUTION, 168, 64, 10, 10)
                    self.GAMEMODE = 2

            if self.GAMEMODE != -1:
                self.ONCE = False

            P.display.update()

            event = P.event.wait()

if __name__ == '__main__':
    MainWindow()

P.quit()
exit()
3 Answers

As that's your entire code, for ease on both of our parts, i will just demonstrate how to create a button with text

you can use the freetype module

import pygame
import pygame.freetype # you have to do this import explicitly

You create a Font object

my_font = pygame.freetypeFont(name='Arial', size=24) 

and then you can render it to create a text surface as well as a rect with the same size

txt_surface, txt_rect = my_font.render(text='Press this Button', 
                                       fgcolor=pg.Color('Black'))

If you have a Button object that has an image and a rect attribute (which it really should) then you can use the txt_rect to align the center and then blit it on the button's image

txt_rect.center = my_button.rect.center
my_button.image.blit(txt_surface, txt_rect)

This way the text surface with "Press this Button" gets drawn to the button's image.

The problem is the function MENU.CENTER_IT(). It is centring the button-text on the screen with respect to the text-image height, and not on the button location.

I modified this function to take the button rectangle as a parameter, and have it calculate a centred position with respect to that rectangle:

def CENTER_IT(self, butt_rect, text):
    #self.CENTERED = (self.CENTER_X-text.get_width()//2, self.CENTER_Y-text.get_height()//2)
    # Centre text over the top of button rectangle
    text_x = butt_rect.x + ( ( butt_rect.width  - text.get_width() )  // 2 )
    text_y = butt_rect.y + ( ( butt_rect.height - text.get_height() ) // 2 )
    self.CENTERED = ( text_x, text_y )

Then the calling code in MENU.VIEW_BUTTONS() needs to pass the button rectangle into the modified centring function:

def VIEW_BUTTONS(self):
    for i in range(0, self.BUTTON_COUNT):
        P.draw.rect(self.WINDOW, vars(self)["BTN"+str(i+1)+"_CX"], vars(self)["BTN"+str(i+1)+"_RECT"])
        self.CENTER_IT(vars(self)["BTN"+str(i+1)+"_RECT"], vars(self)["BTN"+str(i+1)+"_TEXT"])
        self.WINDOW.blit(vars(self)["BTN"+str(i+1)+"_TEXT"], ( self.CENTERED[0], self.CENTERED[1] ) )

This fixes the issue.

Using the python vars functionality to create new variables at runtime makes the code much more difficult to read and debug. These values would be better stored in a simple data structure like a list, or "Button" class.

Your code is not handling the pygame.QUIT event.

I have tried running your code and from what I understand there seems to be a problem with the way how you are drawing the text and buttons on the canvas.

The image reflects the canvas upon changing this line enter image description here self.WINDOW.blit(vars(self)["BTN"+str(i+1)+"_TEXT"], (self.CENTERED[0], self.CENTERED[1]+self.BUTTON_PADDING_Y*(i-1)+150))

So this tells me that for button 1,2 are created after the text and hence are placed on top of the text. You should debug your code and get an understanding of how everything is applied. You could then easily locate the bug.

Related