Spaces:
Runtime error
Runtime error
"""Text formatting, layout and display. | |
This module provides classes for loading styled documents from text files, | |
HTML files and a pyglet-specific markup format. Documents can be styled with | |
multiple fonts, colours, styles, text sizes, margins, paragraph alignments, | |
and so on. | |
Using the layout classes, documents can be laid out on a single line or | |
word-wrapped to fit a rectangle. A layout can then be efficiently drawn in | |
a window or updated incrementally (for example, to support interactive text | |
editing). | |
The label classes provide a simple interface for the common case where an | |
application simply needs to display some text in a window. | |
A plain text label can be created with:: | |
label = pyglet.text.Label('Hello, world', | |
font_name='Times New Roman', | |
font_size=36, | |
x=10, y=10) | |
Alternatively, a styled text label using HTML can be created with:: | |
label = pyglet.text.HTMLLabel('<b>Hello</b>, <i>world</i>', | |
x=10, y=10) | |
Either label can then be drawn at any time with:: | |
label.draw() | |
For details on the subset of HTML supported, see `pyglet.text.formats.html`. | |
Refer to the Programming Guide for advanced usage of the document and layout | |
classes, including interactive editing, embedding objects within documents and | |
creating scrollable layouts. | |
.. versionadded:: 1.1 | |
""" | |
from os.path import dirname as _dirname | |
from os.path import splitext as _splitext | |
import pyglet | |
from pyglet.text import layout, document, caret | |
class DocumentDecodeException(Exception): | |
"""An error occurred decoding document text.""" | |
pass | |
class DocumentDecoder: | |
"""Abstract document decoder. | |
""" | |
def decode(self, text, location=None): | |
"""Decode document text. | |
:Parameters: | |
`text` : str | |
Text to decode | |
`location` : `Location` | |
Location to use as base path for additional resources | |
referenced within the document (for example, HTML images). | |
:rtype: `AbstractDocument` | |
""" | |
raise NotImplementedError('abstract') | |
def get_decoder(filename, mimetype=None): | |
"""Get a document decoder for the given filename and MIME type. | |
If `mimetype` is omitted it is guessed from the filename extension. | |
The following MIME types are supported: | |
``text/plain`` | |
Plain text | |
``text/html`` | |
HTML 4 Transitional | |
``text/vnd.pyglet-attributed`` | |
Attributed text; see `pyglet.text.formats.attributed` | |
`DocumentDecodeException` is raised if another MIME type is given. | |
:Parameters: | |
`filename` : str | |
Filename to guess the MIME type from. If a MIME type is given, | |
the filename is ignored. | |
`mimetype` : str | |
MIME type to lookup, or ``None`` to guess the type from the | |
filename. | |
:rtype: `DocumentDecoder` | |
""" | |
if mimetype is None: | |
_, ext = _splitext(filename) | |
if ext.lower() in ('.htm', '.html', '.xhtml'): | |
mimetype = 'text/html' | |
else: | |
mimetype = 'text/plain' | |
if mimetype == 'text/plain': | |
from pyglet.text.formats import plaintext | |
return plaintext.PlainTextDecoder() | |
elif mimetype == 'text/html': | |
from pyglet.text.formats import html | |
return html.HTMLDecoder() | |
elif mimetype == 'text/vnd.pyglet-attributed': | |
from pyglet.text.formats import attributed | |
return attributed.AttributedTextDecoder() | |
else: | |
raise DocumentDecodeException(f'Unknown format "{mimetype}"') | |
def load(filename, file=None, mimetype=None): | |
"""Load a document from a file. | |
:Parameters: | |
`filename` : str | |
Filename of document to load. | |
`file` : file-like object | |
File object containing encoded data. If omitted, `filename` is | |
loaded from disk. | |
`mimetype` : str | |
MIME type of the document. If omitted, the filename extension is | |
used to guess a MIME type. See `get_decoder` for a list of | |
supported MIME types. | |
:rtype: `AbstractDocument` | |
""" | |
decoder = get_decoder(filename, mimetype) | |
if not file: | |
with open(filename) as f: | |
file_contents = f.read() | |
else: | |
file_contents = file.read() | |
file.close() | |
if hasattr(file_contents, "decode"): | |
file_contents = file_contents.decode() | |
location = pyglet.resource.FileLocation(_dirname(filename)) | |
return decoder.decode(file_contents, location) | |
def decode_html(text, location=None): | |
"""Create a document directly from some HTML formatted text. | |
:Parameters: | |
`text` : str | |
HTML data to decode. | |
`location` : str | |
Location giving the base path for additional resources | |
referenced from the document (e.g., images). | |
:rtype: `FormattedDocument` | |
""" | |
decoder = get_decoder(None, 'text/html') | |
return decoder.decode(text, location) | |
def decode_attributed(text): | |
"""Create a document directly from some attributed text. | |
See `pyglet.text.formats.attributed` for a description of attributed text. | |
:Parameters: | |
`text` : str | |
Attributed text to decode. | |
:rtype: `FormattedDocument` | |
""" | |
decoder = get_decoder(None, 'text/vnd.pyglet-attributed') | |
return decoder.decode(text) | |
def decode_text(text): | |
"""Create a document directly from some plain text. | |
:Parameters: | |
`text` : str | |
Plain text to initialise the document with. | |
:rtype: `UnformattedDocument` | |
""" | |
decoder = get_decoder(None, 'text/plain') | |
return decoder.decode(text) | |
class DocumentLabel(layout.TextLayout): | |
"""Base label class. | |
A label is a layout that exposes convenience methods for manipulating the | |
associated document. | |
""" | |
def __init__(self, document=None, | |
x=0, y=0, z=0, width=None, height=None, | |
anchor_x='left', anchor_y='baseline', | |
multiline=False, dpi=None, batch=None, group=None, rotation=0): | |
"""Create a label for a given document. | |
:Parameters: | |
`document` : `AbstractDocument` | |
Document to attach to the layout. | |
`x` : int | |
X coordinate of the label. | |
`y` : int | |
Y coordinate of the label. | |
`z` : int | |
Z coordinate of the label. | |
`width` : int | |
Width of the label in pixels, or None | |
`height` : int | |
Height of the label in pixels, or None | |
`anchor_x` : str | |
Anchor point of the X coordinate: one of ``"left"``, | |
``"center"`` or ``"right"``. | |
`anchor_y` : str | |
Anchor point of the Y coordinate: one of ``"bottom"``, | |
``"baseline"``, ``"center"`` or ``"top"``. | |
`multiline` : bool | |
If True, the label will be word-wrapped and accept newline | |
characters. You must also set the width of the label. | |
`dpi` : float | |
Resolution of the fonts in this layout. Defaults to 96. | |
`batch` : `~pyglet.graphics.Batch` | |
Optional graphics batch to add the label to. | |
`group` : `~pyglet.graphics.Group` | |
Optional graphics group to use. | |
`rotation`: float | |
The amount to rotate the label in degrees. A positive amount | |
will be a clockwise rotation, negative values will result in | |
counter-clockwise rotation. | |
""" | |
super().__init__(document, width, height, multiline, dpi, batch, group) | |
self._x = x | |
self._y = y | |
self._z = z | |
self._rotation = rotation | |
self._anchor_x = anchor_x | |
self._anchor_y = anchor_y | |
self._update() | |
def text(self): | |
"""The text of the label. | |
:type: str | |
""" | |
return self.document.text | |
def text(self, text): | |
self.document.text = text | |
def color(self): | |
"""Text color. | |
Color is a 4-tuple of RGBA components, each in range [0, 255]. | |
:type: (int, int, int, int) | |
""" | |
return self.document.get_style('color') | |
def color(self, color): | |
self.document.set_style(0, len(self.document.text), {'color': color}) | |
def opacity(self): | |
"""Blend opacity. | |
This property sets the alpha component of the colour of the label's | |
vertices. With the default blend mode, this allows the layout to be | |
drawn with fractional opacity, blending with the background. | |
An opacity of 255 (the default) has no effect. An opacity of 128 will | |
make the label appear semi-translucent. | |
:type: int | |
""" | |
return self.color[3] | |
def opacity(self, alpha): | |
if alpha != self.color[3]: | |
self.color = list(map(int, (*self.color[:3], alpha))) | |
def font_name(self): | |
"""Font family name. | |
The font name, as passed to :py:func:`pyglet.font.load`. A list of names can | |
optionally be given: the first matching font will be used. | |
:type: str or list | |
""" | |
return self.document.get_style('font_name') | |
def font_name(self, font_name): | |
self.document.set_style(0, len(self.document.text), {'font_name': font_name}) | |
def font_size(self): | |
"""Font size, in points. | |
:type: float | |
""" | |
return self.document.get_style('font_size') | |
def font_size(self, font_size): | |
self.document.set_style(0, len(self.document.text), {'font_size': font_size}) | |
def bold(self): | |
"""Bold font style. | |
:type: bool | |
""" | |
return self.document.get_style('bold') | |
def bold(self, bold): | |
self.document.set_style(0, len(self.document.text), {'bold': bold}) | |
def italic(self): | |
"""Italic font style. | |
:type: bool | |
""" | |
return self.document.get_style('italic') | |
def italic(self, italic): | |
self.document.set_style(0, len(self.document.text), {'italic': italic}) | |
def get_style(self, name): | |
"""Get a document style value by name. | |
If the document has more than one value of the named style, | |
`pyglet.text.document.STYLE_INDETERMINATE` is returned. | |
:Parameters: | |
`name` : str | |
Style name to query. See documentation for | |
`pyglet.text.layout` for known style names. | |
:rtype: object | |
""" | |
return self.document.get_style_range(name, 0, len(self.document.text)) | |
def set_style(self, name, value): | |
"""Set a document style value by name over the whole document. | |
:Parameters: | |
`name` : str | |
Name of the style to set. See documentation for | |
`pyglet.text.layout` for known style names. | |
`value` : object | |
Value of the style. | |
""" | |
self.document.set_style(0, len(self.document.text), {name: value}) | |
def __del__(self): | |
self.delete() | |
class Label(DocumentLabel): | |
"""Plain text label. | |
""" | |
def __init__(self, text='', | |
font_name=None, font_size=None, bold=False, italic=False, stretch=False, | |
color=(255, 255, 255, 255), | |
x=0, y=0, z=0, width=None, height=None, | |
anchor_x='left', anchor_y='baseline', | |
align='left', | |
multiline=False, dpi=None, batch=None, group=None, rotation=0): | |
"""Create a plain text label. | |
:Parameters: | |
`text` : str | |
Text to display. | |
`font_name` : str or list | |
Font family name(s). If more than one name is given, the | |
first matching name is used. | |
`font_size` : float | |
Font size, in points. | |
`bold` : bool/str | |
Bold font style. | |
`italic` : bool/str | |
Italic font style. | |
`stretch` : bool/str | |
Stretch font style. | |
`color` : (int, int, int, int) | |
Font colour, as RGBA components in range [0, 255]. | |
`x` : int | |
X coordinate of the label. | |
`y` : int | |
Y coordinate of the label. | |
`z` : int | |
Z coordinate of the label. | |
`width` : int | |
Width of the label in pixels, or None | |
`height` : int | |
Height of the label in pixels, or None | |
`anchor_x` : str | |
Anchor point of the X coordinate: one of ``"left"``, | |
``"center"`` or ``"right"``. | |
`anchor_y` : str | |
Anchor point of the Y coordinate: one of ``"bottom"``, | |
``"baseline"``, ``"center"`` or ``"top"``. | |
`align` : str | |
Horizontal alignment of text on a line, only applies if | |
a width is supplied. One of ``"left"``, ``"center"`` | |
or ``"right"``. | |
`multiline` : bool | |
If True, the label will be word-wrapped and accept newline | |
characters. You must also set the width of the label. | |
`dpi` : float | |
Resolution of the fonts in this layout. Defaults to 96. | |
`batch` : `~pyglet.graphics.Batch` | |
Optional graphics batch to add the label to. | |
`group` : `~pyglet.graphics.Group` | |
Optional graphics group to use. | |
`rotation`: float | |
The amount to rotate the label in degrees. A positive amount | |
will be a clockwise rotation, negative values will result in | |
counter-clockwise rotation. | |
""" | |
doc = decode_text(text) | |
super().__init__(doc, x, y, z, width, height, anchor_x, anchor_y, multiline, dpi, batch, group, rotation) | |
self.document.set_style(0, len(self.document.text), { | |
'font_name': font_name, | |
'font_size': font_size, | |
'bold': bold, | |
'italic': italic, | |
'stretch': stretch, | |
'color': color, | |
'align': align, | |
}) | |
class HTMLLabel(DocumentLabel): | |
"""HTML formatted text label. | |
A subset of HTML 4.01 is supported. See `pyglet.text.formats.html` for | |
details. | |
""" | |
def __init__(self, text='', location=None, | |
x=0, y=0, z=0, width=None, height=None, | |
anchor_x='left', anchor_y='baseline', | |
multiline=False, dpi=None, batch=None, group=None, rotation=0): | |
"""Create a label with an HTML string. | |
:Parameters: | |
`text` : str | |
HTML formatted text to display. | |
`location` : `Location` | |
Location object for loading images referred to in the | |
document. By default, the working directory is used. | |
`x` : int | |
X coordinate of the label. | |
`y` : int | |
Y coordinate of the label. | |
`z` : int | |
Z coordinate of the label. | |
`width` : int | |
Width of the label in pixels, or None | |
`height` : int | |
Height of the label in pixels, or None | |
`anchor_x` : str | |
Anchor point of the X coordinate: one of ``"left"``, | |
``"center"`` or ``"right"``. | |
`anchor_y` : str | |
Anchor point of the Y coordinate: one of ``"bottom"``, | |
``"baseline"``, ``"center"`` or ``"top"``. | |
`multiline` : bool | |
If True, the label will be word-wrapped and render paragraph | |
and line breaks. You must also set the width of the label. | |
`dpi` : float | |
Resolution of the fonts in this layout. Defaults to 96. | |
`batch` : `~pyglet.graphics.Batch` | |
Optional graphics batch to add the label to. | |
`group` : `~pyglet.graphics.Group` | |
Optional graphics group to use. | |
`rotation`: float | |
The amount to rotate the label in degrees. A positive amount | |
will be a clockwise rotation, negative values will result in | |
counter-clockwise rotation. | |
""" | |
self._text = text | |
self._location = location | |
doc = decode_html(text, location) | |
super().__init__(doc, x, y, z, width, height, anchor_x, anchor_y, multiline, dpi, batch, group, rotation) | |
def text(self): | |
"""HTML formatted text of the label. | |
:type: str | |
""" | |
return self._text | |
def text(self, text): | |
self._text = text | |
self.document = decode_html(text, self._location) | |