text rendering
This commit is contained in:
parent
b9d2468194
commit
3bfa6e0eaf
@ -2,5 +2,6 @@ from .doodles import Doodle, Group
|
||||
from .lines import Line
|
||||
from .shapes import Circle, Rectangle
|
||||
from .color import Color
|
||||
from .text import Text
|
||||
|
||||
__all__ = [Doodle, Group, Line, Circle, Rectangle, Color]
|
||||
__all__ = [Doodle, Group, Line, Circle, Rectangle, Color, Text]
|
||||
|
42
src/doodles/examples/words.py
Normal file
42
src/doodles/examples/words.py
Normal file
@ -0,0 +1,42 @@
|
||||
import random
|
||||
import itertools
|
||||
from doodles import Group, Circle, Color, Text
|
||||
|
||||
# TODO: depending on system these fonts often do not have all the
|
||||
# necessary characters, find 3 widely available fonts that do
|
||||
Text.make_font("small", 16, "mono")
|
||||
Text.make_font("medium", 24, "copperplate")
|
||||
Text.make_font("large", 48, "papyrus")
|
||||
print(Text._fonts)
|
||||
|
||||
# Via ChatGPT
|
||||
hello_world = [
|
||||
"Hello, World!", # English
|
||||
"Hola, Mundo!", # Spanish
|
||||
"Bonjour, Monde!", # French
|
||||
"Hallo, Welt!", # German
|
||||
"Ciao, Mondo!", # Italian
|
||||
"こんにちは、世界!", # Japanese
|
||||
"안녕하세요, 세계!", # Korean
|
||||
"Привет, мир!", # Russian
|
||||
"Olá, Mundo!", # Portuguese
|
||||
"नमस्ते, दुनिया!", # Hindi
|
||||
"Merhaba, Dünya!", # Turkish
|
||||
"Salam, Dünya!", # Azerbaijani
|
||||
"Hej, Världen!", # Swedish
|
||||
"Hei, Maailma!", # Finnish
|
||||
"שלום, עולם!", # Hebrew
|
||||
"Szia, Világ!", # Hungarian
|
||||
"Zdravo, Svijete!", # Bosnian
|
||||
"Sawubona, Mhlaba!", # Zulu
|
||||
"Marhaba, Alalam!", # Arabic
|
||||
"你好,世界!", # Mandarin Chinese
|
||||
]
|
||||
|
||||
|
||||
def create():
|
||||
# use a generator to include each one 3x
|
||||
for greeting in itertools.chain.from_iterable(itertools.repeat(hello_world, 3)):
|
||||
Text().random().font(
|
||||
random.choice(("small", "medium", "large"))
|
||||
).text(greeting)
|
85
src/doodles/text.py
Normal file
85
src/doodles/text.py
Normal file
@ -0,0 +1,85 @@
|
||||
import pygame
|
||||
from .doodles import Doodle
|
||||
|
||||
# TOOD: make configurable
|
||||
DEFAULT_FONT_SIZE = 24
|
||||
|
||||
class Text(Doodle):
|
||||
# Having each bit of text on the screen load a separate copy
|
||||
# of its font would be wasteful, since the most common case would
|
||||
# be for most text to use the same font.
|
||||
#
|
||||
# The solution here is to use a class attribute, shared by *all* instances
|
||||
# of the class.
|
||||
#
|
||||
# This is an implementation of the Flyweight design pattern, which
|
||||
# allows multiple objects to share some state.
|
||||
#
|
||||
# This can quickly become a mess if the shared state is mutable,
|
||||
# note that here, once a font is loaded it does not change.
|
||||
# This avoids nearly all pitfalls associated with this approach.
|
||||
_fonts: dict[str, pygame.font.Font] = {}
|
||||
|
||||
# this method is attached to the class `Text`, not individual instances
|
||||
# like normal methods (which take self as their implicit parameter)
|
||||
@classmethod
|
||||
def make_font(cls, name, size, font=None, bold=False, italic=False):
|
||||
"""
|
||||
The way fonts work in most graphics libraries requires choosing a font
|
||||
size, as well as any variation (bold, italic) at the time of creation.
|
||||
|
||||
It would be nice if we could allow individual Text objects vary these,
|
||||
but doing so would be much more complex or require significantly more
|
||||
memory.
|
||||
"""
|
||||
if font is None:
|
||||
font = pygame.font.Font(None, size)
|
||||
else:
|
||||
path = pygame.font.match_font(font, bold=bold, italic=italic)
|
||||
font = pygame.font.Font(path, size)
|
||||
cls._fonts[name] = font
|
||||
|
||||
@classmethod
|
||||
def get_font(cls, name=None):
|
||||
if not name:
|
||||
# None -> default font
|
||||
# load on demand
|
||||
if None not in cls._fonts:
|
||||
cls._fonts[None] = pygame.font.Font(None, DEFAULT_FONT_SIZE)
|
||||
return cls._fonts[None]
|
||||
else:
|
||||
return cls._fonts[name]
|
||||
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
Text will be centered at `pos`
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self._text = ""
|
||||
self._rendered = None
|
||||
self._font = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"Text(pos={self.pos_vec}, text={self._text}, parent={self._parent})"
|
||||
|
||||
def draw(self, screen):
|
||||
text_rect = self._rendered.get_rect(center=(self.x, self.y))
|
||||
screen.blit(self._rendered, text_rect)
|
||||
|
||||
def text(self, text: str) -> "Doodle":
|
||||
"""
|
||||
A setter for the text
|
||||
"""
|
||||
self._text = text
|
||||
# text needs to be rendered once on change to be performant
|
||||
# doing this in draw would be much slower since it is called
|
||||
# much more often than the text changes
|
||||
if not self._font:
|
||||
self._font = self.get_font() # default font
|
||||
self._rendered = self._font.render(self._text, True, self._color)
|
||||
return self
|
||||
|
||||
def font(self, font: str) -> "Doodle":
|
||||
# TODO: error checking
|
||||
self._font = self._fonts[font]
|
||||
return self
|
Loading…
Reference in New Issue
Block a user