pygame – How to display text with font & color?

Question:

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?

Asked By: Max Hudson

||

Answers:

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))
Answered By: veiset

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. 🙂

Answered By: Technohazard

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)
Answered By: mechanicarts

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

Answered By: ninMonkey

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()
Answered By: Rabbid76

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, nttext: {self.text} ntFontFamly:{self.font} ntColor: {self.color} ntSize: {self.size} ntPos: {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.

Answered By: srattigan

I wrote a TextElement class to handle text placement. It’s still has room for improvement. One thing to improve is to add fallback fonts using SysFont in case the font asset isn’t available.

import os
from typing import Tuple, Union
from pygame.font import Font
from utils.color import Color


class TextElement:
    TEXT_SIZE = 50

    def __init__(self, surface, size=TEXT_SIZE, color=Color('white'), font_name='Kanit-Medium') -> None:
        self.surface = surface
        self._font_name = font_name
        self._size = size
        self.color = color
        self.font = self.__initialize_font()

    @property
    def font_name(self):
        return self._font_name

    @font_name.setter
    def font_name(self, font_name):
        self._font_name = font_name
        self.font = self.__initialize_font()

    @font_name.deleter
    def font_name(self):
        del self._font_name

    @property
    def size(self):
        return self._size

    @size.setter
    def size(self, size):
        self._size = size
        self.font = self.__initialize_font()

    @size.deleter
    def size(self):
        del self._size

    def write(self, text: str, coordinates: Union[str, Tuple[int, int]] = 'center'):
        rendered_text = self.font.render(text, True, self.color)
        if isinstance(coordinates, str):
            coordinates = self.__calculate_alignment(rendered_text, coordinates)
        self.surface.blit(rendered_text, coordinates)
        return self


    def __calculate_alignment(self, rendered_text, alignment):
        # https://www.pygame.org/docs/ref/surface.html#pygame.Surface.get_rect
        # Aligns rendered_text to the surface at the given alignment position
        # e.g: rendered_text.get_rect(center=self.surface.get_rect().center)
        alignment_coordinates = getattr(self.surface.get_rect(), alignment)
        return getattr(rendered_text, 'get_rect')(**{alignment: alignment_coordinates}).topleft

    def __initialize_font(self):
        return Font(os.path.join(
            'assets', 'fonts', f'{self._font_name}.ttf'), self._size)

Here is how you can use it:

  TextElement(self.screen, 80).write('Hello World!', 'midtop')
  TextElement(self.screen).write('Hello World 2!', (250, 100))

  # OR

  text = TextElement(self.screen, 80)
  text.size = 100
  text.write('Bigger text!', (25, 50))
  text.write('Bigger text!', 'midbottom')

I hope this can help someone! Cheers!

Answered By: Gabriel Araujo
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.