Search code examples
python-3.xpygamepygame-surface

texts are not shown on pygame screen


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:

enter image description here

My pygame result:

enter image description here

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?


Solution

  • 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).