Button widget in Pygame updated

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
Avatar My youtube channel

Twitter: @pythonprogrammi - python_pygame

Videos

Speech recognition game

Pygame's Platform Game

Other Pygame's posts

Published by pythonprogramming

Started with basic on the spectrum, loved javascript in the 90ies and python in the 2000, now I am back with python, still making some javascript stuff when needed.