PyGameExamplesAndAnswers

StackOverflow            reply.it reply.it

“A long descriptive name is better than a short enigmatic name. A long descriptive name is better than a long descriptive comment.”
Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship


Text and font

Bugs

Related Stack Overflow questions:

Update text

Font

Related Stack Overflow questions:

Minimal pygame.font module example

repl.it/@Rabbid76/PyGame-Text

Minimal pygame.freetype module example

inimal `pygame.freetype` module example

repl.it/@Rabbid76/PyGame-FreeTypeText

Minimal `pygame.freetype` module example

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

Find font

Related Stack Overflow questions:

Use pygame.font.SysFont instead of pygame.font.Font:

font = pygame.font.SysFont('comicsansms', size)

Or use pygame.font.match_font() to find the path to a specific font file:

comicsansms_file = pygame.font.match_font('comicsansms')
font = pygame.font.Font(comicsansms_file, size)

Bold

Related Stack Overflow questions:

To draw a”bold” text the “bold” version of the font must be used. “bold” isn’t a flag or attribute, it’s just a different font. Use pygame.font.match_font() to find the path to a specific font file.
With the pygame.freetype modle the text can be rendered with different styles like STYLE_DEFAULT and STYLE_STRONG. However, the text can only be rendered with one style at a time.

Unicode

Related Stack Overflow questions:

I want to use Pygame’s freetype module to load a colorful emoji via its unicode. Unfortunately I only get a monochrome image with the outline of the emoji:

How to load colorful emojis in pygame?

import pygame
import pygame.freetype

pygame.init()
window = pygame.display.set_mode((200, 200))

seguisy80 = pygame.freetype.SysFont("segoeuisymbol", 100)
emoji, rect = seguisy80.render('😃', "black")
rect.center = window.get_rect().center

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

    window.fill("lightgray")
    window.blit(emoji, rect)
    pygame.display.flip()

pygame.quit()

How can I modify this code to get a full RGBA color image of an emoji?

Unfortunately, colored font loading is not natively supported in Pygame. However, there is a workaround. First you need a colored emoji font. For example, you can download one here: Apple Color Emoji for Linux.

Load this font using https://freetype.org/. Install freetype-py:

pip3 install freetype-py 

For Windows users, it should be mentioned that the installed package does not support the font and results in an “unimplemented feature” exception. Download the package from Unofficial Windows Binaries for Python Extension Packages and install it. e.g.:

pip3 install freetype_py-2.2.0-cp310-cp310-win_amd64.whl

Now you’re prepared and can load an emoji from the font.Emojis and their Unicode can be found here: Emoticons (Unicode block). Copy the emoji or use the unicode and load the glyph:

import freetype

face = freetype.Face("AppleColorEmoji.ttf")
face.set_char_size(int(face.available_sizes[-1].size)) 
    
face.load_char('😀', freetype.FT_LOAD_COLOR) # or face.load_char('\U0001F603', freetype.FT_LOAD_COLOR)

The loaded glyph now needs to be turned into a pygame.Surface. To do this, use NumPy. How this works in detail is explained in the answer to the question: How do I convert an OpenCV (cv2) image (BGR and BGRA) to a pygame.Surface object.

import numpy as np

ft_bitmap = face.glyph.bitmap
bitmap = np.array(ft_bitmap.buffer, dtype=np.uint8).reshape((ft_bitmap.rows, ft_bitmap.width, 4))
bitmap[:, :, [0, 2]] = bitmap[:, :, [2, 0]]
emoji = pygame.image.frombuffer(bitmap.flatten(), (ft_bitmap.width, ft_bitmap.rows), 'RGBA')

How to load colorful emojis in pygame?

import pygame
import freetype
import numpy as np

class Emojis:
    def __init__(self):
        self. face = freetype.Face("AppleColorEmoji.ttf")
        self.face.set_char_size(int(self.face.available_sizes[-1].size)) 
    def create_surface(self, unicode):
        self.face.load_char(unicode, freetype.FT_LOAD_COLOR)
        ft_bitmap = self.face.glyph.bitmap
        bitmap = np.array(ft_bitmap.buffer, dtype=np.uint8).reshape((ft_bitmap.rows, ft_bitmap.width, 4))
        bitmap[:, :, [0, 2]] = bitmap[:, :, [2, 0]]
        return pygame.image.frombuffer(bitmap.flatten(), (ft_bitmap.width, ft_bitmap.rows), 'RGBA')

pygame.init()
window = pygame.display.set_mode((200, 200))
emojis = Emojis()

emoji = emojis.create_surface('😃')
#emoji = emojis.create_surface('\U0001F603')
rect = emoji.get_rect(center = window.get_rect().center)

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

    window.fill("lightgray")
    window.blit(emoji, rect)
    pygame.display.flip()

pygame.quit()

Align text and text size

Related Stack Overflow questions:

pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. The position of the rectangle can be specified by a keyword argument. For example, the center of the rectangle can be specified with the keyword argument center. These keyword argument are applied to the attributes of the pygame.Rect before it is returned (see pygame.Rect for a full list of the keyword arguments).

Get the text rectangle and place the center of the text rectangle on the center of the window rectangle:

text_rect = text.get_rect(center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))

You can even get the center of the window from the display Surface :

text_rect = text.get_rect(center = screen.get_rect().center)

Or with the use of pygame.display.get_surface():

text_rect = text.get_rect(center = pygame.display.get_surface().get_rect().center)

A Surface can be drawn on another Surface using the blit method. The second argument is either a tuple (x, y) representing the upper left corner or a rectangle. With a rectangle, only the upper left corner of the rectangle is taken into account. Therefore you can pass the text rectangle directly to blit:

screen.blit(text, text_rect)

📁 minimal example - Render centered text

python library pygame: centering text

Table

Related Stack Overflow questions:v

Blinking text

Related Stack Overflow questions:

Text

Related Stack Overflow questions:

Use the KEYDOWN event to get the input from the keyboard (see pygame.event). The key that was pressed can be obtained from the key attribute of the pygame.event.Event object. unicode contains a single character string that is the fully translated character. Add the character to the text when a key is pressed.
Two special keys need to be dealt with. If RETURN is pressed, the input is finished. If BACKSPACE is pressed, the last character of the input text must be removed:

if event.type == pygame.KEYDOWN and input_active:
    if event.key == pygame.K_RETURN:
        input_active = False
    elif event.key == pygame.K_BACKSPACE:
        text =  text[:-1]
    else:
        text += event.unicode

📁 minimal example - Text input

How to create a text input box with pygame?

Use the algorithm in a pygame.sprite.Sprite class. Handle the event in the update method.Determine whether the mouse clicks in the text entry field with collidepoint (see How to detect when a rectangular object, image or sprite is clicked) and activate the text input box:

class TextInputBox(pygame.sprite.Sprite):
    # [...]

    def update(self, event_list):
        for event in event_list:
            if event.type == pygame.MOUSEBUTTONDOWN and not self.active:
                self.active = self.rect.collidepoint(event.pos)
            if event.type == pygame.KEYDOWN and self.active:
                if event.key == pygame.K_RETURN:
                    self.active = False
                elif event.key == pygame.K_BACKSPACE:
                    self.text = self.text[:-1]
                else:
                    self.text += event.unicode
                self.render_text()

Pass the list of events to the update method of the Group that contains the Sprite:

event_list = pygame.event.get()
for event in event_list:
    if event.type == pygame.QUIT:
        run = False
group.update(event_list)

📁 minimal example - Text input box

How to create a text input box with pygame?

There is no automatic solution. You have to implement the text wrap by yourself and draw the text line by line respectively word by word.
Fortunately PyGame wiki provides a function that for this task. See PyGame wiki Simple Text Wrapping for pygame.

I’ve extended the function and added an additional argument, which provides left or right aligned text, centerd text or even block mode:

📁 Minimal example - Word wrap
📁 Minimal example - Word wrap

repl.it/@Rabbid76/PyGame-TextWrap

text text

textAlignLeft = 0
textAlignRight = 1
textAlignCenter = 2
textAlignBlock = 3

def drawText(surface, text, color, rect, font, align=textAlignLeft, aa=False, bkg=None):
    lineSpacing = -2
    spaceWidth, fontHeight = font.size(" ")[0], font.size("Tg")[1]

    listOfWords = text.split(" ")
    if bkg:
        imageList = [font.render(word, 1, color, bkg) for word in listOfWords]
        for image in imageList: image.set_colorkey(bkg)
    else:
        imageList = [font.render(word, aa, color) for word in listOfWords]

    maxLen = rect[2]
    lineLenList = [0]
    lineList = [[]]
    for image in imageList:
        width = image.get_width()
        lineLen = lineLenList[-1] + len(lineList[-1]) * spaceWidth + width
        if len(lineList[-1]) == 0 or lineLen <= maxLen:
            lineLenList[-1] += width
            lineList[-1].append(image)
        else:
            lineLenList.append(width)
            lineList.append([image])

    lineBottom = rect[1]
    lastLine = 0
    for lineLen, lineImages in zip(lineLenList, lineList):
        lineLeft = rect[0]
        if align == textAlignRight:
            lineLeft += + rect[2] - lineLen - spaceWidth * (len(lineImages)-1)
        elif align == textAlignCenter:
            lineLeft += (rect[2] - lineLen - spaceWidth * (len(lineImages)-1)) // 2
        elif align == textAlignBlock and len(lineImages) > 1:
            spaceWidth = (rect[2] - lineLen) // (len(lineImages)-1)
        if lineBottom + fontHeight > rect[1] + rect[3]:
            break
        lastLine += 1
        for i, image in enumerate(lineImages):
            x, y = lineLeft + i*spaceWidth, lineBottom
            surface.blit(image, (round(x), y))
            lineLeft += image.get_width() 
        lineBottom += fontHeight + lineSpacing

    if lastLine < len(lineList):
        drawWords = sum([len(lineList[i]) for i in range(lastLine)])
        remainingText = ""
        for text in listOfWords[drawWords:]: remainingText += text + " "
        return remainingText
    return ""

Performance issues an lag

Related Stack Overflow questions:

Creating a font object is very time consuming because the font file has to be read and interpreted.

Transparent text

Related Stack Overflow questions:

When using the pygame.font module, the alpha channel of the text color is not taken into account when rendering a text, but see pygame.font.Font.render:

Antialiased images are rendered to 24-bit RGB images. If the background is transparent a pixel alpha will be included.

and pygame.Surface.set_alpha

Changed in pygame 2.0: per-surface alpha can be combined with per-pixel alpha.

Hence it is completely sufficient to set the transparency after rendering the text with set_alpha. This even works for anti-aliased text:

font = pygame.font.SysFont(None, 150)

text_surf = font.render('test text', True, (255, 0, 0))
text_surf.set_alpha(127)

window.blit(text_surf, (x, y))

📁 minimal example - Render transparent text with font module

repl.it/@Rabbid76/PyGame-TransparentText

Python - Pygame - rendering translucent text

By using the pygame.freetype module, you can use a transparent color directly when creating a text surface:

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

text_surf2, text_rect2 = ft_font.render('test text', (255, 0, 0, 128))

window.blit(text_surf2, (x, y))

Or if you are rendering the text directly onto a surface:

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

ft_font.render_to(window, (x, y), 'test text', (255, 0, 0, 128))

📁 minimal example - Render transparent text with freetype module

repl.it/@Rabbid76/PyGame-TransparentFreeTypeText

Python - Pygame - rendering translucent text

📁 minimal example - Render transparent text with transparent background

repl.it/@Rabbid76/PyGame-TextTransparentBackground

Python - Pygame - rendering text with transparent background

Anti-Aliasing

Related Stack Overflow questions:

Devanagari/Arabic/Persian text

Related Stack Overflow questions:

Ascii image

Related Stack Overflow questions:

Typewriter

Related Stack Overflow questions:

Scroll Text

Related Stack Overflow questions: