Boltamist
Boltamist

Reputation: 79

Resizing text with buttons in pygame

So, I have been tasked with creating a game as part of a project. In my program, I was thinking of having an options menu where you can adjust the size of the text in the program. When I was trying to do this on my own, I used a class for the button creation and in that class, I have a variable called "textsize" which changes the size of the text. Basically, I draw the buttons and add the functionality to press them completely fine, the issue arises when I change the integer in the variable "textsize" as it doesn't actually change the text size in the buttons. Trying to update the pygame screen with Pygame.display.update() and pygame.display.flip() does not work to refresh the size of the buttons. Did anyone get a quick and easy solution?

The code is here:

pygame.init()

textsize = 20

class button():
    def __init__(self, colour, x, y, width, height, text = ""):

        self.colour = colour
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.text = text

    def draw(self, screen, outline = None):

        if outline:
            pygame.draw.rect(screen, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)

        pygame.draw.rect(screen, self.colour, (self.x, self.y, self.width, self.height), 0)

        if self.text != "":
            font = pygame.font.SysFont('segoeuisemibold', textsize)
            text = font.render(self.text, 1, (0, 0, 0))
            screen.blit(text, (self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))

    def isOver(self, pos):
        if pos[0] > self.x and pos[0] < self.x + self.width:
            if pos[1] > self.y and pos[1] < self.y + self.height:
                return True         
        return False


def OptionsMenu():

    screen = pygame.display.set_mode((800, 600))

    textsize1 = button((0, 0, 0), 100, 150, 200, 50, "Set Text Size: 14")
    textsize2 = button((0, 0, 0), 500, 150, 200, 50, "Set Text Size: 16")
    textsize3 = button((0, 0, 0), 100, 300, 200, 50, "Set Text Size: 18")
    textsize4 = button((0, 0, 0), 500, 300, 200, 50, "Set Text Size: 20")

    while True:
        screen.fill((100, 100, 100))
        textsize1.draw(screen, (0, 0, 0))
        textsize2.draw(screen, (0, 0, 0))
        textsize3.draw(screen, (0, 0, 0))
        textsize4.draw(screen, (0, 0, 0))
        pygame.display.update()

        for event in pygame.event.get():
            pos = pygame.mouse.get_pos()

        if event.type == pygame.QUIT:
            pygame.quit()

        if event.type == pygame.MOUSEBUTTONDOWN:
            if textsize1.isOver(pos):
                textsize = 14

            if textsize2.isOver(pos):
                textsize = 16
                textsize2.draw(screen, (0, 0, 0))
            if textsize3.isOver(pos):
                textsize = 18
                pygame.display.flip()
            if textsize4.isOver(pos):
                textsize = 20
                pygame.display.flip()

        if event.type == pygame.MOUSEMOTION:
            if textsize1.isOver(pos):
                textsize1.colour = (35, 65, 145)
            else:
                textsize1.colour = (70, 105, 150)

            if textsize2.isOver(pos):
                textsize2.colour = (35, 65, 145)
            else:
                textsize2.colour = (70, 105, 150)

            if textsize3.isOver(pos):
                textsize3.colour = (35, 65, 145)
            else:
                textsize3.colour = (70, 105, 150)

            if textsize4.isOver(pos):
                textsize4.colour = (35, 65, 145)
            else:
                textsize4.colour = (70, 105, 150)

OptionsMenu()

Upvotes: 2

Views: 758

Answers (1)

sloth
sloth

Reputation: 101122

When you create the font:

font = pygame.font.SysFont('segoeuisemibold', textsize)

textsize refers to the global variable you defined at the top.

But when you set a value to textsize in the OptionsMenu function:

    if event.type == pygame.MOUSEBUTTONDOWN:
        if textsize1.isOver(pos):
            textsize = 14

you're actually creating another new variable textsize and don't change the value the button class reads.

An easy fix is to explicitly tell python that you want to access the global variable.

...
def OptionsMenu():

    global textsize # tell python you want to access the global variable textsize 

    screen = pygame.display.set_mode((800, 600))

...

enter image description here


But I would probably restructure to code to something like this:

import pygame
pygame.init()

class GuiSettings:
    def __init__(self):
        self.textsize = 20
        self.button_color = (35, 65, 145)
        self.button_color_hover = (70, 105, 150)

class Button():
    def __init__(self, x, y, width, height, outline, settings, text = "", action = None):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.text = text
        self.settings = settings
        self.action = action
        self.outline = outline

    def draw(self, screen, outline = None):

        if self.outline:
            pygame.draw.rect(screen, self.outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)


        color = self.settings.button_color if not self.isOver(pygame.mouse.get_pos()) else self.settings.button_color_hover
        pygame.draw.rect(screen, color, (self.x, self.y, self.width, self.height), 0)

        if self.text != "":
            font = pygame.font.SysFont('segoeuisemibold', self.settings.textsize)
            text = font.render(self.text, 1, (0, 0, 0))
            screen.blit(text, (self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))

    def update(self, events):
        for event in events:
            if event.type == pygame.MOUSEBUTTONDOWN \
               and self.isOver(pygame.mouse.get_pos()) \
               and self.action:
                self.action()

    def isOver(self, pos):
        if pos[0] > self.x and pos[0] < self.x + self.width:
            if pos[1] > self.y and pos[1] < self.y + self.height:
                return True         
        return False


def OptionsMenu():

    screen = pygame.display.set_mode((800, 600))
    settings = GuiSettings()

    buttons = [
        Button(100, 150, 200, 50, (0, 0, 0), settings, "Set Text Size: 14", lambda: settings.__setattr__('textsize', 14)),
        Button(500, 150, 200, 50, (0, 0, 0), settings, "Set Text Size: 16", lambda: settings.__setattr__('textsize', 16)),
        Button(100, 300, 200, 50, (0, 0, 0), settings, "Set Text Size: 18", lambda: settings.__setattr__('textsize', 18)),
        Button(500, 300, 200, 50, (0, 0, 0), settings, "Set Text Size: 20", lambda: settings.__setattr__('textsize', 20))
    ]

    while True:
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT:
                pygame.quit()
                return

        for button in buttons:
            button.update(events)

        screen.fill((100, 100, 100))
        for button in buttons:
            button.draw(screen)

        pygame.display.flip()


OptionsMenu()

This way, all the button logic is handled in the Button class itself; the code becomes reusable.

Another thing to improve is the fact that currently, each frame the font is loaded four times. Not a big deal in this simple example; but font loading and rendering is quite expensive and can be a major performance drain.

Upvotes: 4

Related Questions