'pygame - How to display text with font & color?

Is there a way I can display text on a pygame window using python?

I need to display a bunch of live information that updates and would rather not make an image for each character I need.

Can I blit text to the screen?



Solution 1:[1]

Yes. It is possible to draw text in pygame:

# initialize font; must be called after 'pygame.init()' to avoid 'Font not Initialized' error
myfont = pygame.font.SysFont("monospace", 15)

# render text
label = myfont.render("Some text!", 1, (255,255,0))
screen.blit(label, (100, 100))

Solution 2:[2]

You can use your own custom fonts by setting the font path using pygame.font.Font

pygame.font.Font(filename, size): return Font

example:

pygame.font.init()
font_path = "./fonts/newfont.ttf"
font_size = 32
fontObj = pygame.font.Font(font_path, font_size)

Then render the font using fontObj.render and blit to a surface as in veiset's answer above. :)

Solution 3:[3]

I have some code in my game that displays live score. It is in a function for quick access.

def texts(score):
   font=pygame.font.Font(None,30)
   scoretext=font.render("Score:"+str(score), 1,(255,255,255))
   screen.blit(scoretext, (500, 457))

and I call it using this in my while loop:

texts(score)

Solution 4:[4]

There are 2 possibilities. In either case PyGame has to be initialized by pygame.init.

import pygame
pygame.init()

Use either the pygame.font module and create a pygame.font.SysFont or pygame.font.Font object. render() a pygame.Surface with the text and blit the Surface to the screen:

my_font = pygame.font.SysFont(None, 50)
text_surface = myfont.render("Hello world!", True, (255, 0, 0))
screen.blit(text_surface, (10, 10))

Or use the pygame.freetype module. Create a pygame.freetype.SysFont() or pygame.freetype.Font object. render() a pygame.Surface with the text or directly render_to() the text to the screen:

my_ft_font = pygame.freetype.SysFont('Times New Roman', 50)
my_ft_font.render_to(screen, (10, 10), "Hello world!", (255, 0, 0))

See also Text and font


Minimal pygame.font example: repl.it/@Rabbid76/PyGame-Text

import pygame

pygame.init()
window = pygame.display.set_mode((500, 150))
clock = pygame.time.Clock()

font = pygame.font.SysFont(None, 100)
text = font.render('Hello World', True, (255, 0, 0))

background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *window.get_size(), (128, 128, 128), (64, 64, 64)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
for rect, color in tiles:
    pygame.draw.rect(background, color, rect)

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.blit(background, (0, 0))
    window.blit(text, text.get_rect(center = window.get_rect().center))
    pygame.display.flip()

pygame.quit()
exit()

Minimal pygame.freetype example: repl.it/@Rabbid76/PyGame-FreeTypeText

import pygame
import pygame.freetype

pygame.init()
window = pygame.display.set_mode((500, 150))
clock = pygame.time.Clock()

ft_font = pygame.freetype.SysFont('Times New Roman', 80)

background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *window.get_size(), (128, 128, 128), (64, 64, 64)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
for rect, color in tiles:
    pygame.draw.rect(background, color, rect)

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.blit(background, (0, 0))
    text_rect = ft_font.get_rect('Hello World')
    text_rect.center = window.get_rect().center
    ft_font.render_to(window, text_rect.topleft, 'Hello World', (255, 0, 0))
    pygame.display.flip()

pygame.quit()
exit()

Solution 5:[5]

I wrote a wrapper, that will cache text surfaces, only re-render when dirty. googlecode/ninmonkey/nin.text/demo/

Solution 6:[6]

I wrote a TextBox class. It can use many custom fonts relatively easily and specify colors. I wanted to have text in several places on the screen, some of which would update such as lives, scores (of all players) high score, time passed and so on.

Firstly, I created a fonts folder in the project and loaded in the fonts I wanted to use. As an example, I had 'arcade.ttf' in my fots folder. When making an instance of the TextBox, I could specify that font using the fontlocation (optional) arg.

e.g.

self.game_over_text = TextBox("GAME OVER", 100, 80, 420, RED, 'fonts/arcade.ttf')

I found making the text and updating it each time "clunky" so my solution was an update_text method.

For example, updating the Player score:

self.score1_text.update_text(f'{self.p1.score}')

It could be refactored to accept a list of str, but it suited my needs for coding a version of "S

# -*- coding: utf-8 -*-
'''
 @author:   srattigan
 @date:     22-Mar-2022
 @project:  TextBox class example
 @description:  A generic text box class 
            to simplify text objects in PyGame
            Fonts can be downloaded from
            https://www.dafont.com/ 
            and other such sites.
'''

# imports
import pygame

# initialise and globals
WHITE = (255, 255, 255)
pygame.font.init() # you have to call this at the start

 
class TextBox:
    '''
    A text box class to simplify creating text in pygame
    '''
    def __init__(self, text, size, x=50, y=50, color=WHITE, fontlocation=None):
        '''
        Constuctor
        text: str, the text to be displayed
        size: int, the font size
        x: int, x-position on the screen
        y: int, y-position on the screen
        color: tuple of int representing color, default is (255,255,255)
        fontlocation: str, location of font file.  If None, default system font is used.
        '''
        pygame.font.init()
        self.text = text
        self.size = size
        self.color = color
        self.x = x
        self.y = y
        if fontlocation == None:
            self.font = pygame.font.SysFont('Arial', self.size)
        else:
            self.font = pygame.font.Font(fontlocation, self.size)

    def draw(self, screen):
        '''
        Draws the text box to the screen passed.
        screen: a pygame Surface object
        '''
        text_surface = self.font.render(f'{self.text}', False, self.color)
        screen.blit(text_surface, [self.x, self.y])

    def update_text(self, new_text):
        '''
        Modifier- Updates the text variable in the textbox instance
        new_text: str, the updated str for the instance.
        '''
        if not isinstance(new_text, str):
            raise TypeError("Invalid type for text object")
        self.text = new_text

    def set_position(self, x, y):
        '''
        Modifier- change or set the position of the txt box
        x: int, x-position on the screen
        y: int, y-position on the screen
        '''
        self.x = x
        self.y = y

    def __repr__(self):
        rep = f'TextBox instance, \n\ttext: {self.text} \n\tFontFamly:{self.font} \n\tColor: {self.color} \n\tSize: {self.size} \n\tPos: {self.x, self.y}'
        return rep

if __name__ == "__main__":
    test = TextBox("Hello World", 30, 30, 30)
    print(test)

To use this in my Game class

from textbox import TextBox

and in the initialisation part of the game, something like this:

self.time_text = TextBox("Time Left: 100", 20, 20, 40)
self.cred_text = TextBox("created by Sean R.", 15, 600, 870)
self.score1_text = TextBox("0", 100, 40, 650)
self.score2_text = TextBox("0", 100, 660, 650)
self.lives1_text = TextBox("[P1] Lives: 3", 20, 40, 750)
self.lives2_text = TextBox("[P2] Lives: 3", 20, 660, 750)
self.game_over_text = TextBox("GAME OVER", 100, 80, 420, RED)

self.textbox_list = []
self.textbox_list.append(self.time_text)
self.textbox_list.append(self.cred_text)
self.textbox_list.append(self.score1_text)
self.textbox_list.append(self.score2_text)
self.textbox_list.append(self.lives1_text)
self.textbox_list.append(self.lives2_text)

so that when I want to draw all on the screen:

for txt in self.textbox_list:
    txt.draw(screen)

In the update section of the game, I only update directly the boxes that have updated text using the update_text method- if there is nothing to be updated, the text stays the same.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Technohazard
Solution 3 mechanicarts
Solution 4
Solution 5 ninMonkey
Solution 6