Buttons on different screens in Pygame - pygame

I am making a program with a start and help menu, as well as other functions. The buttons on the "main menu" work perfectly, but once I enter the Help menu, the buttons I have programmed for screen navigation do not work properly. Please feel free to look at the code below. I keep getting an error that says "BackHelpRect not defined". BackHelpRect is a rectangle I have drawn around my image buttons (as for all the rest of my buttons) to sense for when the mouse is touching them. It works for the main menu buttons but once I go into the help menu, the rectangles for those buttons "disappear". If you have a solution, please respond, it would be greatly appreciated. :)
Sorry I have attached my entire code, but everything is called within other functions and it all works in sync with each other. Sorry if it is very long.
import pygame
import time
import smtplib
import webbrowser
pygame.init() # Starts pygame.
white = (255, 255, 255)
blue = (0, 100, 175)
black = (0, 0, 0)# Initialising Python with colours and a window size.
Event = []
AllTasks = []
MenuStatus = ""
def OpenMainMenu(): # To open Main Menu.
MenuStatus = "Main Menu" # In case the code needs to know what menu is currently projected.
pygame.display.set_caption("Main Menu") # Naming the New Window.
CurrentMenu = pygame.image.load(r'D:\Holiday Planner Screen Resources\Main Menu.jpg') # Opening a custom made Screen.
Screen.blit(CurrentMenu, (0, 0))
CreateEventB()
OpenEventB()
HelpB()
pygame.display.update()# Loads the Main Menu Screen.
ButtonInteract()
def CreateEventB(): # To Open the "Create Menu Button".
CreateButton = pygame.image.load(r'D:\Holiday Planner Screen Resources\Create Event.jpg') # Open the custom button design.
CreateButton = pygame.transform.scale(CreateButton, (174, 85))
global CreateRect
CreateRect = Screen.blit(CreateButton, (425, 325))
pygame.display.update(CreateRect)
def OpenEventB(): # To Open the "Create Menu Button".
OpenButton = pygame.image.load(r'D:\Holiday Planner Screen Resources\Open Event.jpg') # Open the custom button design.
OpenButton = pygame.transform.scale(OpenButton, (179, 85))
global OpenRect
OpenRect = Screen.blit(OpenButton, (225, 325))
pygame.display.update(OpenRect)
def HelpB():
HelpButton = pygame.image.load(r'D:\Holiday Planner Screen Resources\Help Button.jpg') # Open the custom button design.
HelpButton = pygame.transform.scale(HelpButton, (151, 51))
global HelpRect
HelpRect = Screen.blit(HelpButton, (335, 415))
pygame.display.update(HelpRect)
def BackHelp():
BackHelpB = pygame.image.load(r'D:\Holiday Planner Screen Resources\BackHelp.jpg') # Open the custom button design.
BackHelpB = pygame.transform.scale(BackHelpB, (235, 84))
global BackHelpRect
BackHelpRect = Screen.blit(BackHelpB, (45, 490))
pygame.display.update(BackHelpRect)
def NextHelp():
NextHelpB = pygame.image.load(r'D:\Holiday Planner Screen Resources\NextHelp.jpg') # Open the custom button design.
NextHelpB = pygame.transform.scale(NextHelpB, (233, 85))
global NextHelpRect
NextHelpRect = Screen.blit(NextHelpB, (545, 490))
pygame.display.update(NextHelpRect)
def FinishHelp():
FinishHelpB = pygame.image.load(r'D:\Holiday Planner Screen Resources\FinishHelp.jpg') # Open the custom button design.
FinishHelpB = pygame.transform.scale(NextHelpB, (230, 85))
global FinishHelpRect
FinishHelpRect = Screen.blit(FinishHelpRect, (545, 490))
pygame.display.update(FinishHelpRect)
def ButtonInteract(): # A function that helps to connect button interactions to their respective functions.
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if CreateRect.collidepoint(event.pos):
CreateEvent()
if event.type == pygame.MOUSEBUTTONDOWN:
if OpenRect.collidepoint(event.pos):
Event, AllTasks = OpenEvent("BerlinTestTrip")
print(Event, AllTasks)
if event.type == pygame.MOUSEBUTTONDOWN:
if HelpRect.collidepoint(event.pos):
MenuStatus = "Help menu 1"
Help()
def ButtonsForHelp(): # A seperate button interaction pathway for help buttons as they are housed on a different screen.
global BackHelpRect
global NextHelpRect
global FinishHelpRect
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if BackHelpRect.collidepoint(event.pos): # Button navigaton.
if MenuStatus == "Help menu 1":
OpenMainMenu()
if MenuStatus == "Help menu 2":
MenuStatus = "Help menu 1"
Help()
if MenuStatus == "Help menu 3":
MenuStatus = "Help menu 2"
Help()
if event.type == pygame.MOUSEBUTTONDOWN:
if NextHelpRect.collidepoint(event.pos): # Button navigaton.
if MenuStatus == "Help menu 1":
MenuStatus = "Help menu 2"
Help()
if MenuStatus == "Help menu 2":
MenuStatus = "Help menu 3"
Help()
if event.type == pygame.MOUSEBUTTONDOWN:
if FinishHelpRect.collidepoint(event.pos): # Button navigaton.
if MenuStatus == "Help menu 3":
OpenMainMenu()
def CreateEvent():
print ("Created")
def OpenEvent(EventSaveName):
print ("Opened")
def Help():
pygame.display.set_caption("Help Menu") # Naming the New Window.
CurrentMenu = pygame.image.load(r'D:\Holiday Planner Screen Resources\Help Menu 1.jpg') # Opening a custom made Screen.
Screen.blit(CurrentMenu, (0, 0))
pygame.display.update()
if MenuStatus == "Help menu 1":
CurrentMenu = pygame.image.load(r'D:\Holiday Planner Screen Resources\Help Menu 1.jpg') # Opening a custom made Screen.
Screen.blit(CurrentMenu, (0, 0))
pygame.display.update()# Loads the menu Screen.
BackHelp()
NextHelp()
if MenuStatus == "Help menu 2":
CurrentMenu = pygame.image.load(r'D:\Holiday Planner Screen Resources\Help Menu 2.jpg') # Opening a custom made Screen.
Screen.blit(CurrentMenu, (0, 0))
pygame.display.update()# Loads the menu Screen.
BackHelp()
NextHelp()
if MenuStatus == "Help menu 3":
CurrentMenu = pygame.image.load(r'D:\Holiday Planner Screen Resources\Help Menu 3.jpg') # Opening a custom made Screen.
Screen.blit(CurrentMenu, (0, 0))
pygame.display.update()# Loads the menu Screen.
BackHelp()
FinishHelp()
ButtonsForHelp()
# MAIN BODY
Screen = pygame.display.set_mode((824, 620))
OpenMainMenu()
pygame.display.update()
Thanks.

The issue is that the main loop is referencing BackHelpRect before it is defined. It's easy to replicate this, just select the "back" menu options on the first screen. To debug this I added a print( "BackHelp Defined HERE" ) to the BackHelp() function. It's not always called beforehand.
To fix this, perhaps your code can define all these Images and Rects at once before the menu processing begins. Something like:
def preloadButtons():
global HelpButton, HelpButtonRect
global BackHelpButton, BackHelpRect
# etc. etc.
HelpButton = pygame.image.load('button.jpg') # Open the custom button design.
HelpButton = pygame.transform.scale(HelpButton, (151, 51))
HelpButtonRect = HelpButton.get_rect()
HelpButtonRect.topleft = (335, 415)
BackHelpButton = pygame.image.load('button.jpg') # Open the custom button design.
BackHelpButton = pygame.transform.scale(BackHelpButton, (235, 84))
BackHelpButtonRect = BackHelpButton.get_rect()
BackHelpButtonRect.topleft = (45, 490)
Storing all these buttons as separate globals is not a very good design, and it's already causing complexity in the drawing and event-testing throughout the rest of the program. It would be better to pack all the attributes (image, size, position, rect) of a button into a data structure. It's doesn't have to be a Python class, just putting each buttons properties into a list would be a step-forward, and then maybe all those lists into a single all_buttons list.
For example, here we define a list of button properties, and then iterate through these to crate a simpler list of button name/image/rect.
help_button = [ 'help', 'help_button.jpg', (151, 51), (335, 415) ]
back_help_button = [ 'back', 'back_button.jpg', (235, 84), ( 45, 490) ]
# ... TODO: rest of buttons
all_buttons_properties = [ help_button, back_help_button ]
# Initialise buttons
all_buttons = []
for butt in all_button_properties:
button_image = pygame.image.load( butt[1] ) # load
button_image = pygame.transform.scale( button_image, butt[2] ) # resize
button_rect = button_image.get_rect()
button_rect.topleft = butt[3] # move
all_buttons.append( butt[0], button_image, button_rect )
This gives you a simple all_buttons list of lists [ button-name, resized-button-image, pre-positioned-button-rect ].
And probably you want if ... elif ... for these buttons tests, not if ... if ...:
if event.type == pygame.MOUSEBUTTONDOWN:
if NextHelpRect.collidepoint(event.pos): # Button navigaton.
if MenuStatus == "Help menu 1":
MenuStatus = "Help menu 2"
Help()
if MenuStatus == "Help menu 2":
MenuStatus = "Help menu 3"
Help()
What happens when MenuStatus equals "Help menu 1" -> it becomes "Help menu 2", but then the very next if changes this again to "Help menu 3"

Related

Video game not responding correctly to input

I'm developing a pygame video game, and everything was working out perfectly until yesterday. The issues began after I formatted my pc. So when i run the game, the first screen to show up is the 'Menu'. So in this state class I have an event method where when you press the 'p' key it gets you to the 'Play' state. So now it is not working, I don't know why.
I've changed nothing. I just formatted my pc and reinstalled python, pygame and pgu module. But the strange thing comes when I reprogram the videogame so that the first state to show up when you run the game is the 'Play' state, everything works perfectly. It also has an event method where when you press the arrows, the character moves, and when the player presses ESC it takes you to the 'Menu' state.
So again when I'm at the 'Menu' state the game doesn't respond to the input I'm giving to it. I don't really know what's happening.
Here is an example of what I was saying in comments :
Sorry if all comments are not appropriate to your level but I wanted to be sure you understand it all.
import pygame
screen = pygame.display.set_mode((1000, 1000))
class Menu:
pass
class Play:
pass
def main():
running = True # here we define the main variable of the main loop
main_menu = True # when the main loop will begin, main_menu will begin too
game = False # game is false because player didn't press p
options = False # options is false because player didn't go to options
while running: # begin the main loop
while main_menu:
for event in pygame.event.get(): # listen for events
if event.type == pygame.QUIT: # if the player quit, ends up the 'main_menu' loop and 'running' loop too
running = False
main_menu = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p: # if p key is pressed, exit from main menu and begin 'game' loop
main_menu = False
game = True
if event.key == pygame.K_o: # if o key is pressed, exit from main menu and begin 'options' loop
main_menu = False
options = True
screen.fill((255, 0, 0)) # I fill the screen in red to make the example more explicit
pygame.display.flip() # I update the screen every frame
while game:
for event in pygame.event.get(): # listen for events
if event.type == pygame.QUIT: # if the player quit, ends up 'running' and 'options' loop
running = False
game = False
if event.type == pygame.KEYDOWN: # listen for keys
if event.key == pygame.K_BACKSPACE: # if the player press backspace (delete), ends up 'game' loop
game = False # and begin (again) the 'main_menu' loop
main_menu = True
screen.fill((255, 255, 255)) # I fill the screen in white to make the example more explicit
pygame.display.flip() # I update the screen every frame
while options: # same logic for this one
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
options = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_BACKSPACE:
options = False
main_menu = True
screen.fill((0, 255, 0))
pygame.display.flip()
pygame.init() # initialize pygame
main() # begin main loop
pygame.quit() # quit pygame

Text box render on top of main code in pygame?

I have implemented text box code to my simulation but now it is not rendering the main code, or rendering on top of my main code so nothing is displayed but the box :(
Here is my text box code :
def textbox(font20):
input_box = pygame.Rect(300,225,400,100)
color_inactive =(TURQUOISE)
color_active = (0,255,249)
color = color_inactive
active = False
text = ""
done = False
font = font20
while not done:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if input_box.collidepoint(event.pos):
active = not active
else:
active = False
color = color_active if active else color_inactive
if event.type == pygame.KEYDOWN:
if active:
if event.key == pygame.K_RETURN:
print(text)
text = ""
done = True
elif event.key == pygame.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
# Render the current text.
text_surface = font.render(text, True, color)
# Resize the box if the text is too long.
width = max(200, text_surface.get_width()+10)
input_box.w = width
# Blit the text.
screen.blit(text_surface, (input_box.x+5, input_box.y+5))
# Blit the input_box rect.
pygame.draw.rect(screen, color, input_box, 2)
pygame.display.flip()
I also screen.blit to turquoise at the bottom where I draw the screen and flip it.
Did you remember to click in the box? ;)
There's also a bunch of minor issues:
The code doesn't have a definition for TURQUOISE.
The loop didn't handle the pygame.QUIT event.
It also needs to propagate this event to the outer-loop
As the box grew, it made a mess, I added a black rectangle to erase the old.
It did not return the text value.
With all these, quite minor, bugs fixed, it works pretty well. But just remember to click in the box first!
import pygame
# Window size
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 400
### initialisation
pygame.init()
screen = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ) )
pygame.display.set_caption("Text Box Test")
BLACK = (0,0,0)
TURQUOISE = (0,154,196)
CYAN = (0,255,249)
def textbox(font20):
#input_box = pygame.Rect(300,225,400,100)
input_box = pygame.Rect(10,10,400,100)
color_background = BLACK # for erasing
color_inactive = TURQUOISE
color_active = CYAN
color = color_inactive
active = False
text = ""
done = False
font = font20
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.event.post( pygame.event.Event( pygame.QUIT ) ) # re-send the quit event to the next loop
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
if input_box.collidepoint(event.pos):
active = not active
else:
active = False
color = color_active if active else color_inactive
elif event.type == pygame.KEYDOWN:
if active:
if event.key == pygame.K_RETURN:
print(text)
#text = ""
done = True
elif event.key == pygame.K_BACKSPACE:
text = text[:-1]
else:
text += event.unicode
print( "DEBUG: text is now [%s]" % ( text ) )
# Render the current text.
text_surface = font.render(text, True, color)
# Resize the box if the text is too long.
width = max(200, text_surface.get_width()+10)
input_box.w = width
# Blit the input_box rect.
pygame.draw.rect(screen, color_background, input_box) # filled box to clear the old one
pygame.draw.rect(screen, color, input_box, 2)
# Blit the text.
screen.blit(text_surface, (input_box.x+5, input_box.y+5))
pygame.display.flip()
return text
font = pygame.font.SysFont('', 24)
textbox( font )
pygame.quit()

What's the most convenient way to add buttons in pygame with text

Following up my last question, for my school assignment, I am developing a game. My concept includes the uses of buttons in this game so I was wondering what is the most convenient way to add buttons in pygame? Also, if you have suggestions for resources which can help me in cases like this, please let me know. Thanks
Here's my Code:
# Program: Import Library, Pygame, for initialization of this program
import pygame
# Initialize the game engine
pygame.init()
# Define Colours
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
GREEN = ( 0, 255, 0)
RED = ( 255, 0, 0)
BLUE = ( 0, 0, 255)
display_width = 1080
display_height = 720
size = (display_width, display_height)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("MiniConomy Trivia, for Adults")
# Button Program
class Button:
def __init__(self, size, text, pos, bgColor=(0, 255, 0), textColor=(0, 0, 0)):
self.pos = pos
self.size = size
self.text = text
self.font = pygame.font.Font(pygame.font.get_default_font(), size[1])
self.textSurf = self.font.render(f"{text}", True, textColor)
self.button = pygame.Surface((size[0], size[1])).convert()
self.button.fill(bgColor)
def render(self, window):
window.blit(self.button, (self.pos[0], self.pos[1]))
window.blit(self.textSurf, (self.pos[0]+1, self.pos[1]+5))
def clicked(self, events):
mousePos = pygame.mouse.get_pos()# get the mouse position
for event in events:
if self.button.get_rect(topleft=self.pos).collidepoint(mousePos[0], mousePos[1]):
if event.type == pygame.MOUSEBUTTONDOWN:
return True
return False
# Setting a Title Screen
def text_objects(text, font):
textSurface = font.render(text, True, BLACK)
return textSurface, textSurface.get_rect()
largeText = pygame.font.Font('freesansbold.ttf', 90)
# Creating a Title Screen
TextSurf, TextRect = text_objects("MiniConomy", largeText)
TextRect.center = (540,150)
# Play Button
button = Button([280,50], "Let's Begin", [380,302])
button2 = Button([280, 50], "Second Button", [380, 302])
#Loop until the user clicks the close button
done = False
# -------- Main Program Loop -----------
while done == False:
events = pygame.event.get()
for event in events: # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
# Set the screen background
screen.fill(BLUE)
screen.blit(TextSurf, TextRect)
while True:
# Button 1 Control
button.render(screen)
if button.clicked(events):
print("Game Logic goes here")
pygame.display.flip()
button2.render(screen)
if button2.clicked(events):
print("Game Logic Here")
pygame.display.flip()
pygame.quit()
quit()
The most convenient way to add a button would be to make a button class. I know you are not using OOP with your code, but taking a class approach would allow you to make multiple buttons with a single class acting as a template for each button.
Lets start by making a class.
Fist we will make an init function to initialise all the attribute variables of our button.
class Button:
def __init__(self, size, text, pos):
self.pos = pos
self.size = size
self.text = text
self.font = pygame.font.Font(pygame.font.get_default_font(), size/2)
self.button = pygame.Surface(size[0], size[1]).convert()
Then we will add methods to our class to give our class some behaviour. We want to render our button, then we want to be able to click our button.
def render(self, window):
window.blit(self.button, (self.pos[0], self.pos[1]))
window.blit(self.textSurf, (self.pos[0]+1, self.pos[1]+5))
def clicked(self, events):
mousePos = pygame.mouse.get_pos()# get the mouse position
for event in events:
if self.button.get_rect(topleft=self.pos).collidepoint(mousePos[0], mousePos[1]):
if event.type == pygame.MOUSEBUTTONDOWN:
return True
return False
With this implemented, your code becomes:
# Program: Import Library, Pygame, for initialization of this program
import pygame
# Initialize the game engine
pygame.init()
# Define Colours
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
GREEN = ( 0, 255, 0)
RED = ( 255, 0, 0)
BLUE = ( 0, 0, 255)
display_width = 1080
display_height = 720
size = (display_width, display_height)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("MiniConomy Trivia, for Adults")
class Button:
def __init__(self, size, text, pos, bgColor=(255, 255, 255), textColor=(0, 0, 0)):
self.pos = pos
self.size = size
self.text = text
self.font = pygame.font.Font(pygame.font.get_default_font(), size[1])
self.textSurf = self.font.render(f"{text}", True, textColor)
self.button = pygame.Surface((size[0], size[1])).convert()
self.button.fill(bgColor)
def render(self, window):
window.blit(self.button, (self.pos[0], self.pos[1]))
window.blit(self.textSurf, (self.pos[0]+1, self.pos[1]+5))
def clicked(self, events):
mousePos = pygame.mouse.get_pos()# get the mouse position
for event in events:
if self.button.get_rect(topleft=self.pos).collidepoint(mousePos[0], mousePos[1]):
if event.type == pygame.MOUSEBUTTONDOWN:
return True
return False
# Setting a Title Screen
def text_objects(text, font):
textSurface = font.render(text, True, BLACK)
return textSurface, textSurface.get_rect()
largeText = pygame.font.Font('freesansbold.ttf', 100)
# Creating a Title Screen
TextSurf, TextRect = text_objects("MiniConomy", largeText)
TextRect.center = (540,150)
# Play Button
button = Button([250, 50], "A button", [50, 50])
#Loop until the user clicks the close button
done = False
# -------- Main Program Loop -----------
while done == False:
events = pygame.event.get()
for event in events: # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
# Set the screen background
screen.fill(BLUE)
screen.blit(TextSurf, TextRect)
button.render(screen)
if button.clicked(events):
print("Your game start logic goes here")
pygame.display.flip()
Now obviously, there is a lot of potential to add extra features to our button class, like color changing when mouse is over the button and things like that. Just to show you the benefits of using a class. Now if you want another button, you can simply make another instance of button class
anotherButton = Button([300, 500], "Second Button", [500, 500])
and in main loop call:
while True:
anotherButton.render(screen)
anotherButton.clicked(events)
And BOOM, you have a second button. Hope i could help.
Edit: Answer to the difficulties you were having. Couple of things.
First, Both buttons are in the same position. Change that.
button = Button([280,50], "Let's Begin", [380,302])
button2 = Button([280, 50], "Second Button", [380, 302])
Third argument is position and as you can see, they are the same.
Second, You already have a while True loop so there is no reason why you should add another while True loop. Just use the first while loop, that is why it is called a game loop
Third, You call pygame.display.flip() twice. This function is responsible for updating the screen every frame. Calling it twice means you are trying to update it twice every frame, which can cause unwanted problems.
while not done:
events = pygame.event.get()
for event in events: # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
# Set the screen background
screen.fill(BLUE)
screen.blit(TextSurf, TextRect)
# Button 1 Control
button.render(screen)
if button.clicked(events):
print("Game Logic goes here")
button2.render(screen)
if button2.clicked(events):
print("Game Logic Here")
pygame.display.flip()

What's the best way to add title to a menu screen in pygame

So, for a school assignment, I need to make a game on Adult Education and so I did but just for the basics, I cannot figure out how to add a title on my menu screen in this game. I have tried many other tutorials and some are pretty easy to understand but for some reason, it ends up not working in my program. What is the best way to add a simple title in pygame?
Heres the code(If you would like to check for any errors I have made):
# Program: Import Library, Pygame, for initialization of this program
import pygame
# Initialize the game engine
pygame.init()
# Define Colours
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
GREEN = ( 0, 255, 0)
RED = ( 255, 0, 0)
BLUE = ( 0, 0, 255)
size = (1080, 720)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("MiniConomy Trivia, for Adults")
#Loop until the user clicks the close button
done = False
# -------- Main Program Loop -----------
while done == False:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True # Flag that we are done so we exit this loop
# Set the screen background
screen.fill(BLUE)
pygame.display.flip()
# Setting a Title Screen
def text_objects(text, font):
textSurface = font.render(text, True, BLACK)
return textSurface, textSurface.get_rect()
largeText = pygame.font.Font('freesansbold.ttf', 100)
# Creating a Title Screen
TextSurf, TextRect = text_objects("MiniConomy", largeText)
TextRect.center = ((display_width/2),(display_height/2))
gameDisplay.blit(TextSurf, TextRect)
The code you have here is mostly syntactically correct (except for a couple of variable names which don't exist) but it isn't in the right order.
the "Creating a title screen" section should be out of the loop, the first two lines:
TextSurf, TextRect = text_objects("MiniConomy", largeText)
TextRect.center = ((display_width/2),(display_height/2))
should be placed before the game loop and then the blit function:
gameDisplay.blit(TextSurf, TextRect)
should be at the end of the game loop but instead of "gameDisplay" write "screen" instead as that is what you called your display at the start of the program.
The function "text_objects" should also go at the start of the program otherwise it cannot be called.
Here's the full modified code: https://pastebin.com/hQDbD2ya
Which produces this title screen:

How to cycle 3 images on a rect button?

Just started learning Python/Pygame watching videos and reading to learn . I would like to see a example code to cycle 3 images on a rect button from a mouse press and return to first image. Really I want the pictures to be three options and return different results. So be able to cycle image be able to select option and option when triggered execute choice.
Example
import pygame
pygame.init()
screen = pygame.display.set_mode((300,200))
# three images
images = [
pygame.Surface((100,100)),
pygame.Surface((100,100)),
pygame.Surface((100,100)),
]
images[0].fill((255,0,0))
images[1].fill((0,255,0))
images[2].fill((0,0,255))
# image size and position
images_rect = images[0].get_rect()
# starting index
index = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
# check mouse position and pressed button
if event.button == 1 and images_rect.collidepoint(event.pos):
# cycle index
index = (index+1) % 3
screen.blit(images[index], images_rect)
pygame.display.flip()
pygame.quit()
Example using class - to create many buttons
import pygame
class Button(object):
def __init__(self, position, size):
self._images = [
pygame.Surface(size),
pygame.Surface(size),
pygame.Surface(size),
]
self._images[0].fill((255,0,0))
self._images[1].fill((0,255,0))
self._images[2].fill((0,0,255))
self._rect = pygame.Rect(position, size)
self._index = 0
def draw(self, screen):
screen.blit(self._images[self._index], self._rect)
def event_handler(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1 and self._rect.collidepoint(event.pos):
self._index = (self._index+1) % 3
pygame.init()
screen = pygame.display.set_mode((320,110))
button1 = Button((5, 5), (100, 100))
button2 = Button((110, 5), (100, 100))
button3 = Button((215, 5), (100, 100))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
button1.event_handler(event)
button2.event_handler(event)
button3.event_handler(event)
button1.draw(screen)
button2.draw(screen)
button3.draw(screen)
pygame.display.flip()
pygame.quit()
If I understood the question correctly, you need a single button that changes the look every time you click on it, and changes its relative function accordingly.
You should be able to solve your problem by creating a class that takes two list and a counter
1) list of images
2) list of functions
3) the counter tells you which image/function is selected.
The functions needs to be built in the class, but you can provide the image that you want in the class argument (actually, you could pass them as arguments, but I don't think is worth it).
Here is the code, I commented some lines with their intended meaning (in line comments)
import pygame
import sys
pygame.init()
width = 600
height = 400
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Magic Buttons")
background = pygame.Surface(screen.get_size())
clock = pygame.time.Clock()
class Buttons:
def __init__(self, posX, posY, image1, image2, image3):
self.image_list = [image1, image2, image3] # static list of images
self.function_list = [self.button_function_1,self.button_function_2,self.button_function_3 ]
self.rect_position = (posX, posY) # this is a tuple to identify the upper left corner of the rectangle of the image
self.button_type = 0 # initial value of the button, both for the function and the image
self.image = pygame.image.load(self.image_list[0]) #default image, index number 0 of image_list
self.rect = pygame.Rect(posX, posY, self.image.get_width(), self.image.get_height()) # create a rectangle object same size of the images
def check(self, pos):
if self.rect.collidepoint(pos) ==True:
self.change_button()
else:
pass
def change_button(self):
self.button_type = (self.button_type +1)%3
self.image = pygame.image.load(self.image_list[self.button_type ]) # load the image relative to button_type
self.function_list[self.button_type -1]() # execute the function relative to the new value of button_type
self.draw_button()
def draw_button(self):
screen.blit(self.image, self.rect_position) # blit the new button image
def button_function_1(self):
print ("function 1 in action")
def button_function_2(self):
print ("function 2 in action")
def button_function_3(self):
print ("function 3 in action")
multibutton = Buttons(100,100,"button1.png","button2.png","button3.png") # create an istance of the button in the x=100, y = 100, with the three image button
while True:
background.fill((0,0,0))
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos() # fetch the position of the mouse
multibutton.check(pos) # check if the mouse is on the button
multibutton.draw_button()
pygame.display.flip()