Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] Button Instance
#1
I'm playing around with making a button class and have come across problem.
As long as I just have one button the cursor works as expected but, if I place two or more the cursor flickers.

Any insight appreciated.
See video



#! /usr/bin/env python3.10

# Do the imports
import pygame
import os

# Get current working directory
path = os.getcwd()

# Initialize pygame
pygame.init()

# Set screen size
screen = pygame.display.set_mode((800,600))

# Set and display the window icon
# game_icon = pygame.image.load(f'{path}/icons/ratt.ico')
# pygame.display.set_icon(game_icon)

# Set a hand cursor
hand = pygame.SYSTEM_CURSOR_HAND
arrow = pygame.SYSTEM_CURSOR_ARROW

# Set and display window title
pygame.display.set_caption('My Game')

# pygame clock for setting frame rate
clock = pygame.time.Clock()

class Button:
    def __init__(self):
        self.text = 'Button'
        self.x = 0
        self.y = 0
        self.font_size = 30
        self.bgcolor = (110, 110, 255)
        self.fgcolor = (255, 255, 255)
        self.border_color = (90, 90, 90)
        self.shadow_color = (180, 180, 180)
        self.pos = pygame.mouse.get_pos()


    def show(self):

        btn_text = pygame.font.SysFont(None, self.font_size)

        button_shadow = pygame.Rect(self.x+3, self.y+3, btn_text.size(self.text)[0]*2, \
                                    btn_text.size(self.text)[1]*2+1)
        border = pygame.Rect(self.x-1, self.y-1, (btn_text.size(self.text)[0]*2)+2, \
                             (btn_text.size(self.text)[1]*2)+3)
        button = pygame.Rect(self.x, self.y, btn_text.size(self.text)[0]*2, btn_text.size(self.text)[1]*2)
        text = btn_text.render(f'{self.text}', True, self.fgcolor)

        if button.collidepoint(self.pos[0], self.pos[1]):
            pygame.mouse.set_cursor(hand)
            self.bgcolor = (150,150,255)
            self.fgcolor = (250,250,255)
        else:
            pygame.mouse.set_cursor(arrow)


        pygame.draw.rect(screen, self.shadow_color, button_shadow)
        pygame.draw.rect(screen, self.border_color, border)
        pygame.draw.rect(screen, self.bgcolor, button)
        screen.blit(text, (self.x+button.width/4, self.y+button.height/4))


# Main program
def main():
    running = True

    while running:
        screen.fill('ivory')

        btn = Button()
        btn.x = 220
        btn.y = 210
        btn.show()

        btn2 = Button()
        btn2.text = 'Button 2'
        btn2.x = 420
        btn2.y = 210
        btn2.show()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False


        pygame.display.update()
        clock.tick(60)

if __name__ == '__main__':
    main()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#2
The problem is that the other button keeps wanting to change the cursor back to the pointer. Here I've used a class variable calledhand_cursorto keep track of when a button has changed the cursor. Check out lines 31 and lines 61 through 64.
#! /usr/bin/env python3.10
 
# Do the imports
import pygame
import os
 
# Get current working directory
path = os.getcwd()
 
# Initialize pygame
pygame.init()
 
# Set screen size
screen = pygame.display.set_mode((800,600))
 
# Set and display the window icon
# game_icon = pygame.image.load(f'{path}/icons/ratt.ico')
# pygame.display.set_icon(game_icon)
 
# Set a hand cursor
hand = pygame.SYSTEM_CURSOR_HAND
arrow = pygame.SYSTEM_CURSOR_ARROW
 
# Set and display window title
pygame.display.set_caption('My Game')
 
# pygame clock for setting frame rate
clock = pygame.time.Clock()
 
class Button:
	hand_cursor = False
	def __init__(self):
		self.text = 'Button'
		self.x = 0
		self.y = 0
		self.font_size = 30
		self.bgcolor = (110, 110, 255)
		self.fgcolor = (255, 255, 255)
		self.border_color = (90, 90, 90)
		self.shadow_color = (180, 180, 180)
		self.pos = pygame.mouse.get_pos()
 
 
	def show(self):
 
		btn_text = pygame.font.SysFont(None, self.font_size)
 
		button_shadow = pygame.Rect(self.x+3, self.y+3, btn_text.size(self.text)[0]*2, \
									btn_text.size(self.text)[1]*2+1)
		border = pygame.Rect(self.x-1, self.y-1, (btn_text.size(self.text)[0]*2)+2, \
							 (btn_text.size(self.text)[1]*2)+3)
		button = pygame.Rect(self.x, self.y, btn_text.size(self.text)[0]*2, btn_text.size(self.text)[1]*2)
		text = btn_text.render(f'{self.text}', True, self.fgcolor)
 
		if button.collidepoint(self.pos[0], self.pos[1]):
			Button.hand_cursor = True
			pygame.mouse.set_cursor(hand)
			self.bgcolor = (150,150,255)
			self.fgcolor = (250,250,255)
		else:
			if Button.hand_cursor == False :
				pygame.mouse.set_cursor(arrow)
			else :
				Button.hand_cursor = False
 
 
		pygame.draw.rect(screen, self.shadow_color, button_shadow)
		pygame.draw.rect(screen, self.border_color, border)
		pygame.draw.rect(screen, self.bgcolor, button)
		screen.blit(text, (self.x+button.width/4, self.y+button.height/4))
 
 
# Main program
def main():
	running = True
 
	while running:
		screen.fill('ivory')
 
		btn = Button()
		btn.x = 220
		btn.y = 210
		btn.show()
 
		btn2 = Button()
		btn2.text = 'Button 2'
		btn2.x = 420
		btn2.y = 210
		btn2.show()
 
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				running = False
 
 
		pygame.display.update()
		clock.tick(60)
 
if __name__ == '__main__':
	main()
Reply
#3
Works for two buttons but not more. I think I will start from scratch and try again. Thanks for teaching me something new about the class variables. I've not really worked with them before.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#4
I guess we also needed to know which button was changing the cursor. Here is my latest version that includes acolide_with_pointerinstance variable to keep track of that. Let me know if this helps. I've also taken the button assignments out of the main loop so that they are not recreated over and over.
#! /usr/bin/env python3.10
  
# Do the imports
import pygame
import os
  
# Get current working directory
path = os.getcwd()
  
# Initialize pygame
pygame.init()
  
# Set screen size
screen = pygame.display.set_mode((800,600))
  
# Set and display the window icon
# game_icon = pygame.image.load(f'{path}/icons/ratt.ico')
# pygame.display.set_icon(game_icon)
  
# Set a hand cursor
hand = pygame.SYSTEM_CURSOR_HAND
arrow = pygame.SYSTEM_CURSOR_ARROW
  
# Set and display window title
pygame.display.set_caption('My Game')
  
# pygame clock for setting frame rate
clock = pygame.time.Clock()
  
class Button:
	hand_cursor = False
	def __init__(self):
		self.text = 'Button'
		self.x = 0
		self.y = 0
		self.font_size = 30
		self.bgcolor = (110, 110, 255)
		self.fgcolor = (255, 255, 255)
		self.border_color = (90, 90, 90)
		self.shadow_color = (180, 180, 180)
		self.collide_with_pointer = False 
  
	def show(self):
  
		btn_text = pygame.font.SysFont(None, self.font_size)
  
		button_shadow = pygame.Rect(self.x+3, self.y+3, btn_text.size(self.text)[0]*2, \
									btn_text.size(self.text)[1]*2+1)
		border = pygame.Rect(self.x-1, self.y-1, (btn_text.size(self.text)[0]*2)+2, \
							 (btn_text.size(self.text)[1]*2)+3)
		button = pygame.Rect(self.x, self.y, btn_text.size(self.text)[0]*2, btn_text.size(self.text)[1]*2)
		text = btn_text.render(f'{self.text}', True, self.fgcolor)
		self.pos = pygame.mouse.get_pos()
		if button.collidepoint(self.pos[0], self.pos[1]):
			self.collide_with_pointer = True
			Button.hand_cursor = True
			pygame.mouse.set_cursor(hand)
			self.bgcolor = (150,150,255)
			self.fgcolor = (250,250,255)
		elif self.collide_with_pointer == True :
			self.collide_with_pointer = False
			pygame.mouse.set_cursor(arrow)
			self.bgcolor = (110, 110, 255)
			self.fgcolor = (255, 255, 255)

		pygame.draw.rect(screen, self.shadow_color, button_shadow)
		pygame.draw.rect(screen, self.border_color, border)
		pygame.draw.rect(screen, self.bgcolor, button)
		screen.blit(text, (self.x+button.width/4, self.y+button.height/4))

# Main program
def main():
	btn1 = Button()
	btn1.text = 'Button 1'
	btn1.x = 220
	btn1.y = 210

	btn2 = Button()
	btn2.text = 'Button 2'
	btn2.x = 420
	btn2.y = 210

	btn3 = Button()
	btn3.text = 'Button 3'
	btn3.x = 220
	btn3.y = 280

	btn4 = Button()
	btn4.text = 'Button 4'
	btn4.x = 420
	btn4.y = 280

	running = True
  
	while running:

		screen.fill('ivory')
		btn1.show()
		btn2.show()
		btn3.show()
		btn4.show()

		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				running = False
  
		pygame.display.update()
		clock.tick(60)
  
if __name__ == '__main__':
	main()
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020