Reputation: 79
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
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))
...
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