pygame - How to display text with font & color?

PythonPygame

Python Problem Overview


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?

Python Solutions


Solution 1 - Python

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 - Python

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 - Python

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 - Python

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 - Python

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

Solution 6 - Python

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMax HudsonView Question on Stackoverflow
Solution 1 - PythonveisetView Answer on Stackoverflow
Solution 2 - PythonTechnohazardView Answer on Stackoverflow
Solution 3 - PythonmechanicartsView Answer on Stackoverflow
Solution 4 - PythonRabbid76View Answer on Stackoverflow
Solution 5 - PythonninMonkeyView Answer on Stackoverflow
Solution 6 - PythonsrattiganView Answer on Stackoverflow