
I made some changes to this code to add a command to every button in an easy way: adding a parameter as argument of the class Button that contains the function that does what that button is made for.
Here is the code. If you do not provide a function as argument, there will be a warning telling you how to do it.
When you run the script you get these buttons. They looks pretty and when you mouseover them they change the color. Ok, now let’s see what happens when we click the buttons (only the first one got the bind to the command function.

Here is what happens when you press the 3 button.
The first one has the parameter (command1) to link it to the function command1. This function has the code to change the text of the button, from rome to pressed and also prints something on the terminal.
When you press the other 2, you get the warning, because they haven’ assigned a function yet.
Pretty 😎, ah?

The bind method
I was thinking… why don’t add a binding method, alternative to passing the function as argument? Said and done. here is how you can bind the second button, for example:
button3.bind(lambda: print("Hello"))
This is alternative to the other method. You can alway also do:
button2.command = (lambda: print("I am Milan button"))
The result will be the same, but this way you can add or change the command even after the istanciation of the widget.
import pygame, sys
def help_empty_command():
''' function for buttons without command argument '''
print(f"""
[WARNING]:
You have not assigned a function to
your button. Create a function called command2, for
example and add command2 as last parameter of the istance
of this buttonex:
button1 = Button('Rome',200,40,(100,200),5, command1))""")
buttons = []
class Button:
def __init__(self,text,width,height,pos,elevation, command=help_empty_command):
#Core attributes
self.pressed = False
self.elevation = elevation
self.dynamic_elecation = elevation
self.original_y_pos = pos[1]
self.command = command
# top rectangle
self.top_rect = pygame.Rect(pos,(width,height))
self.top_color = '#475F77'
# bottom rectangle
self.bottom_rect = pygame.Rect(pos,(width,height))
self.bottom_color = '#354B5E'
#text
self.text = text
self.text_surf = gui_font.render(text,True,'#FFFFFF')
self.text_rect = self.text_surf.get_rect(center = self.top_rect.center)
buttons.append(self)
def change_text(self, newtext):
self.text_surf = gui_font.render(newtext, True,'#FFFFFF')
self.text_rect = self.text_surf.get_rect(center = self.top_rect.center)
def bind(self, command):
self.command = command
def draw(self):
# elevation logic
self.top_rect.y = self.original_y_pos - self.dynamic_elecation
self.text_rect.center = self.top_rect.center
self.bottom_rect.midtop = self.top_rect.midtop
self.bottom_rect.height = self.top_rect.height + self.dynamic_elecation
pygame.draw.rect(screen,self.bottom_color, self.bottom_rect,border_radius = 12)
pygame.draw.rect(screen,self.top_color, self.top_rect,border_radius = 12)
screen.blit(self.text_surf, self.text_rect)
self.check_click()
def check_click(self):
mouse_pos = pygame.mouse.get_pos()
if self.top_rect.collidepoint(mouse_pos):
self.top_color = '#D74B4B'
if pygame.mouse.get_pressed()[0]:
self.dynamic_elecation = 0
self.pressed = True
self.change_text(f"{self.text}")
else:
self.dynamic_elecation = self.elevation
if self.pressed == True:
self.command()
self.pressed = False
self.change_text(self.text)
else:
self.dynamic_elecation = self.elevation
self.top_color = '#475F77'
def command1():
print("The first button has some code")
button1.text = "Pressed"
pygame.init()
screen = pygame.display.set_mode((500,500))
pygame.display.set_caption('Gui Menu')
clock = pygame.time.Clock()
gui_font = pygame.font.Font(None,30)
button1 = Button('Rome',200,40,(100,200),5, command1)
button2 = Button('Milan',200,40,(100,250),5)
button2.command = (lambda: print("I am Milan button"))
button3 = Button('Neaples',200,40,(100,300),5)
button3.bind(lambda: print("Hello"))
def buttons_draw():
for b in buttons:
b.draw()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill('#DCDDD8')
buttons_draw()
pygame.display.update()
clock.tick(60)
The repository
https://github.com/formazione/pygame_button
https://github.com/formazione/pygame_widgets
A button using only surfaces and the class Sprite
import pygame
screen = pygame.display.set_mode((800, 600))
buttons = pygame.sprite.Group()
class Button(pygame.sprite.Sprite):
''' Create a button clickable with changing hover color'''
def __init__(self, text="Click",
pos=(0,0), fontsize=16,
colors="white on blue", hover_colors="red on green",
command=lambda: print("No command activated for this button")):
super().__init__()
self.text = text
self.command = command
self.colors = colors
self.original_colors = colors
self.fg, self.bg = self.colors.split(" on ")
self.fgh, self.bgh = hover_colors.split(" on ")
self.font = pygame.font.SysFont("Arial", fontsize)
self.pos = pos
self.create_original()
self.create_hover_image()
def create_original(self):
self.image = self.create_bg(self.text, self.fg, self.bg)
self.original_image = self.image.copy()
def create_hover_image(self):
self.hover_image = self.create_bg(self.text, self.fgh, self.bgh)
self.pressed = 1
buttons.add(self)
def create_bg(self, text, fg, bg):
self.text = text
image = self.font.render(self.text, 1, fg)
self.rect = image.get_rect()
self.rect.x, self.rect.y = self.pos
bgo = pygame.Surface((self.rect.w, self.rect.h))
bgo.fill(bg)
bgo.blit(image, (0,0))
return bgo
def update(self):
''' CHECK IF HOVER AND IF CLICK THE BUTTON '''
if self.rect.collidepoint(pygame.mouse.get_pos()):
self.image = self.hover_image
self.check_if_click()
else:
self.image = self.original_image
def check_if_click(self):
''' checks if you click on the button and makes the call to the action just one time'''
if self.rect.collidepoint(pygame.mouse.get_pos()):
if pygame.mouse.get_pressed()[0] and self.pressed == 1:
# print("Execunting code for button '" + self.text + "'")
self.command()
self.pressed = 0
if pygame.mouse.get_pressed() == (0,0,0):
self.pressed = 1
if __name__ == "__main__":
# Hello, this is a snippet
pygame.init()
pygame.display.set_caption('Example of button')
screen = pygame.display.set_mode((1000, 800))
clock = pygame.time.Clock()
def window():
b1 = Button("CLICK ME", pos=(100,100),
fontsize=36,
colors="red on green",
hover_colors="green on red",
command=lambda: print("clicked right now"))
window()
is_running = True
while is_running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
is_running = False
# to show buttons created
buttons.update()
buttons.draw(screen)
pygame.display.update()
clock.tick(60)
pygame.quit()
The old post
Subscribe to the newsletter for updates
Tkinter templatesTwitter: @pythonprogrammi - python_pygame
Claude's Games
1. Memory gameVideos
Speech recognition gamePygame's Platform Game