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 templates
My youtube channel
Twitter: @pythonprogrammi - python_pygame