I'm trying to create this reference piano by using pygame. I created a Button class and tried to annotate them but only 1 button has annotation. How can I annotate all of the buttons?
Referance piano:
My pygame result:
My pygame script:
import pygame, sys
pygame.init()
BUTTONWIDTH = 75
BUTTONHEIGHT = 300
SHARPSCALE = 2
SHARPWIDTH = BUTTONWIDTH // SHARPSCALE
SHARPHEIGHT = BUTTONHEIGHT // SHARPSCALE
DISPLAYWIDTH = BUTTONWIDTH * 7
DISPLAYHEIGHT = BUTTONHEIGHT
DISPLAYSURF = pygame.display.set_mode((DISPLAYWIDTH,DISPLAYHEIGHT))
DISPLAYRECT = DISPLAYSURF.get_rect()
CLOCK = pygame.time.Clock()
FRAMERATE = 60
FONT = pygame.font.SysFont(None, 45)
class Button(pygame.sprite.Sprite):
def __init__(self,width,height,text,mode,**pos):
super().__init__()
if mode == 'normal' : self.colors = {'on_click':'#bcbcbc', 'on_button':'#eeeeee', 'on_normal':'#ffffff'}
elif mode == 'sharp' : self.colors = {'on_click':'#4f5b62', 'on_button':'#263238', 'on_normal':'#000a12'}
else : raise NameError('Unknown button mode')
self.image = pygame.Surface((width,height))
self.rect = self.image.get_rect(**pos)
self.color = self.colors['on_normal']
self.image.fill(self.color)
text_color = 'black' if mode=='normal' else 'white'
self.text_surf = FONT.render(text, True, text_color)
self.text_rect = self.text_surf.get_rect(midtop=self.rect.center)
self.image.blit(self.text_surf, self.text_rect)
def update(self):
pos = pygame.mouse.get_pos()
#keys = pygame.key.get_pressed()
if self.rect.collidepoint(pos):
left, _ ,_ = pygame.mouse.get_pressed()
if left : self.color = self.colors['on_click' ]
else : self.color = self.colors['on_button']
else : self.color = self.colors['on_normal']
self.image.fill(self.color)
self.image.blit(self.text_surf, self.text_rect)
Buttons = pygame.sprite.Group()
C = Button(BUTTONWIDTH, BUTTONHEIGHT, 'C' , mode='normal', topleft=DISPLAYRECT.topleft)
C_ = Button(SHARPWIDTH , SHARPHEIGHT , 'C#', mode='sharp' , midtop =C.rect.topright)
D = Button(BUTTONWIDTH, BUTTONHEIGHT, 'D' , mode='normal', topleft=C.rect.topright)
D_ = Button(SHARPWIDTH , SHARPHEIGHT , 'D#', mode='sharp' , midtop =D.rect.topright)
E = Button(BUTTONWIDTH, BUTTONHEIGHT, 'E' , mode='normal', topleft=D.rect.topright)
F = Button(BUTTONWIDTH, BUTTONHEIGHT, 'F' , mode='normal', topleft=E.rect.topright)
F_ = Button(SHARPWIDTH , SHARPHEIGHT , 'F#', mode='sharp' , midtop =F.rect.topright)
G = Button(BUTTONWIDTH, BUTTONHEIGHT, 'G' , mode='normal', topleft=F.rect.topright)
G_ = Button(SHARPWIDTH , SHARPHEIGHT , 'G#', mode='sharp' , midtop =G.rect.topright)
A = Button(BUTTONWIDTH, BUTTONHEIGHT, 'A' , mode='normal', topleft=G.rect.topright)
A_ = Button(SHARPWIDTH , SHARPHEIGHT , 'A#', mode='sharp' , midtop =A.rect.topright)
B = Button(BUTTONWIDTH, BUTTONHEIGHT, 'B' , mode='normal', topleft=A.rect.topright)
Buttons.add(C,D,E,F,G,A,B,C_,D_,F_,G_,A_)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
Buttons.draw(DISPLAYSURF)
Buttons.update()
pygame.display.update()
CLOCK.tick(FRAMERATE)
Optinal question: How can I avoid pressing both normal and sharp buttons?
You don't draw the text on the screen. You draw the text on in the button. Therefore the position of the text needs to be set in the coordinate system of the button Surface. The top left of the button on the screen has an absolute coordinate. However, the top left of a Surface is always (0, 0).
self.text_rect = self.text_surf.get_rect(midtop=self.rect.center)
self.text_rect = self.text_surf.get_rect(midtop=self.image.get_rect().center)
Note, if you have a Surface with a size of 100x100 on the screen at 200x200, then the center of the Surface is (250, 250).
If you want to put some text on the screen in the center of the Surface, you have to draw it at (250, 250).
However, if you want to draw the text on the Surface itself (which is 100 x 100 size) in the center, you need to draw it at (50, 50).