显示文本

pyglet提供了 font 用于高效呈现高质量抗锯齿Unicode字形的模块。Pyglet可以使用操作系统上安装的任何字体,也可以在应用程序中提供自己的字体。

请注意,并非所有字体格式都受支持,请参阅 支持的字体格式

文本呈现是使用 text 模块,它可以显示自动换行的格式化文本。它还支持使用插入符号对屏幕上的文本进行交互编辑。

简单的文本呈现

下面的完整示例创建一个窗口,该窗口垂直和水平居中显示“Hello,World”:

window = pyglet.window.Window()
label = pyglet.text.Label('Hello, world',
                          font_name='Times New Roman',
                          font_size=36,
                          x=window.width//2, y=window.height//2,
                          anchor_x='center', anchor_y='center')

@window.event
def on_draw():
    window.clear()
    label.draw()

pyglet.app.run()

该示例演示了文本呈现的最常见用法:

  • 字体名称和大小直接在构造函数中指定。还有其他参数用于设置文本的粗体和斜体样式以及颜色。

  • 文本的位置由 xy 坐标。这些坐标的含义由 anchor_xanchor_y 参数。

  • 将文本实际绘制到屏幕上是使用 pyglet.text.Label.draw() 方法。还可以将标签添加到图形批次;请参阅 批处理渲染 了解更多细节。

这个 HTMLLabel() 类的用法类似,但接受的是HTML格式的字符串,而不是描述样式的参数。这允许标签以混合样式显示文本::

label = pyglet.text.HTMLLabel(
    '<font face="Times New Roman" size="4">Hello, <i>world</i></font>',
    x=window.width//2, y=window.height//2,
    anchor_x='center', anchor_y='center')

看见 格式化文本 有关受支持的HTML子集的详细信息,请参见。

文档/布局模型

这个 Label() 上面演示的类为pyglet的完整文本呈现功能提供了一个简化的接口。潜在的 TextLayout()AbstractDocument 类为pyglet的所有文本特性提供了一个“模型/视图”接口。

../_images/text_classes.png

文件

A document 是体系结构的“模型”部分,描述了要显示的文本的内容和样式。有两个具体的文档类: UnformattedDocumentFormattedDocumentUnformattedDocument 对只包含一种样式的文本的文档进行建模,而 FormattedDocument 允许在文本中更改样式。

可以通过直接构造这两个类中的任何一个来创建空的、未设置样式的文档。但是,通常情况下,您需要使用一些文本来初始化文档。这个 decode_text()decode_attributed()decode_html() 函数返回给定的源字符串的文档。为 decode_text() ,这只是一个纯文本字符串,返回值是 UnformattedDocument **

document = pyglet.text.decode_text('Hello, world.')

decode_attributed()decode_html() 将在下一节中详细介绍。

文档的文本可以直接作为对象的属性进行修改::

document.text = 'Goodbye, cruel world.'

但是,如果对文档进行了较小的更改,则使用 delete_text()insert_text() 方法而不是。

布局

文档的实际布局和呈现由 TextLayout() 上课。这种分割的存在是为了降低代码的复杂性,并允许在多个布局中同时显示单个文档(换句话说,多个布局可以显示一个文档)。

每一位 TextLayout() 类以相同的方式执行布局,但表示在更新效率与绘图和内存使用效率之间进行了权衡。

基地 TextLayout() 类使用很少的内存,并与其他 TextLayout() 同一批中的实例(请参见 批处理渲染 )。如果修改了文档的文本或样式,或者更改了布局约束(例如,更改了布局的宽度),则会重新计算整个文本布局。这可能是一项代价高昂的操作,尤其是对于长文档而言。这使得 TextLayout() 适用于相对较短或不变的文档。

ScrollableTextLayout 是对 TextLayout() 这将剔除指定视图矩形之外的文本,并允许在该矩形内滚动文本,而无需再次执行布局计算。由于此裁剪矩形,图形组不能与其他文本布局共享,因此为了获得理想的性能 ScrollableTextLayout 应仅在需要滚动时使用。

IncrementalTextLayout 使用更复杂的布局算法,对文档的微小更改执行更少的工作。例如,如果用户正在编辑文档,则在键入或删除字符时,仅重新计算直接受影响的文本行。 IncrementalTextLayout 还会执行视图矩形剔除,从而在文档大于视图时减少所需的布局和渲染量。 IncrementalTextLayout 应用于大型文档或快速更改的文档。

所有布局类都可以在给定文档和显示尺寸的情况下构建:

layout = pyglet.text.layout.TextLayout(document, width, height)

构造函数的其他参数允许指定图形批次和组(如果要呈现许多布局,建议使用),以及可选的 multilinewrap_lines 旗帜。

multiline

要支持文档中的换行符,您需要将其设置为 True 。如果不这样做,则换行符将呈现为纯空格。

wrap_lines

如果您希望文档行宽于显示宽度,则通过将此选项设置为 True 。请注意,换行仅在文本中有空格时才起作用,因此它可能不适合没有空格的语言。

与标签一样,布局通过其 xyanchor_xanchor_y 属性。请注意,与 AbstractImage vt.的. anchor 属性接受字符串,如 "bottom""center" 而不是数字位移。

格式化文本

这个 FormattedDocument 类维护文本中单个字符的样式信息,而不是整个文档的单一样式。可以按名称访问和修改样式,例如::

# Get the font name used at character index 0
font_name = document.get_style('font_name', 0)

# Set the font name and size for the first 5 characters
document.set_style(0, 5, dict(font_name='Arial', font_size=12))

在内部,字符样式是在文档文本上进行游程编码的;因此,样式更改很少的较长文档不会占用太多内存。

从文档的角度来看,没有预定义的样式名称:它只是将名称和字符范围映射到任意的Python值。它是 TextLayout 类解释此样式信息;例如,通过基于 font_name 风格。无法识别的样式名称将被布局忽略--您可以使用此知识将附加数据与文档文本一起存储(例如,超链接后面的URL)。

字符样式

以下是所有人都能识别的字符样式 TextLayout() 上课。

如果属性被标记为“as a Distance”,则假定该值以像素为单位(如果以整型或浮点型给出),否则为以下形式的字符串 "0u" 是必需的,其中 0 是距离和 u 是单位;其中之一 "px" (像素), "pt" (积分), "pc" (Picas), "cm" (厘米), "mm" (毫米)或 "in" (英寸)。例如, "14pt" 是覆盖14个点的距离,在默认DPI为96时为18个像素。

font_name

字体系列名称,指定给 pyglet.font.load()

font_size

字体大小,以磅为单位。

bold

布尔型。

italic

布尔型。

underline

范围(0,255)中的整数数组,提供RGBA下划线颜色,或无(默认),表示不带下划线。

kerning

要在字形之间插入的附加空格,如距离。默认为0。

baseline

字形基线与直线基线的偏移量,如距离。正值表示上标,负值表示下标。默认为0。

color

范围(0,255)中的四元组整数,提供RGBA文本颜色

background_color

范围(0,255)中的四元组整数,提供RGBA文本背景颜色;或 None 用于无背景填充。

段落样式

虽然 FormattedDocument 不区分字符级别样式和段落级别样式, TextLayout() 仅在段落级别解释以下样式。您应该注意仅为完整段落设置这些样式,例如,使用 set_paragraph_style()

这些样式对于不带 multiline 设置了标志。

align

"left" (默认), "center""right"

indent

要在段落第一行的第一个字形之前插入的附加水平空格,如距离。

leading

在段落内的连续行之间插入的附加空格,如距离。默认为0。

line_spacing

距离段落中连续基线之间的距离,如距离默认为 None ,它根据最大字体升降自动计算每行的最紧行距。

margin_left

左段边距,如距离。

margin_right

右段页边距,如距离。

margin_top

段落上方的页边距,如距离。

margin_bottom

段落下方的页边距,如距离。相邻页边距不会折叠。

tab_stops

从文本布局的左边缘测量的水平制表位列表,以距离表示。默认为空列表。当制表位用完时,它们隐式地以50像素的间隔继续。

wrap

布尔型。如果为True(默认值),则文本在布局的宽度内换行。

就这些属性而言,段落由换行符(U+0010)或分段符(U+2029)拆分。可以使用字符U+2028强制段落内的换行符。

制表符

pyglet文本中的制表符被解释为“移到下一个制表位”。制表位是以像素为单位指定的,而不是某种字体单位;默认情况下,每50个像素就有一个制表位,因此制表位对于大字体来说可能太小,对于小字体来说可能太大。

此外,在使用选项卡呈现文本时 monospace 字体,字符框不能垂直对齐。

要避免这些可视化问题,更简单的解决方案是将制表符转换为空格,然后再将字符串发送到与文本相关的pyglet类。

属性文本

Pyglet提供了两种用于从纯文本解码格式化文档的格式。它们对于加载预先准备好的文档(如帮助屏幕)很有用。目前还没有用于保存(编码)格式化文档的工具。

这个 attributed text 格式是特定于pyglet的编码,它可以准确地描述任何 FormattedDocument 。您必须使用此编码来访问pyglet文本布局的所有功能。有关更易访问但功能较差的编码,请参阅 HTML 编码,如下所述。

下面的示例显示了一个简单的属性文本编码文档:

Chapter 1

My father's family name being Pirrip, and my Christian name Philip,
my infant tongue could make of both names nothing longer or more
explicit than Pip.  So, I called myself Pip, and came to be called
Pip.

I give Pirrip as my father's family name, on the authority of his
tombstone and my sister - Mrs. Joe Gargery, who married the
blacksmith.  As I never saw my father or my mother, and never saw
any likeness of either of them (for their days were long before the
days of photographs), my first fancies regarding what they were
like, were unreasonably derived from their tombstones.

换行符将被忽略,除非连续出现两个换行符,表示换段。属性强制换行符 \\ 顺序:

This is the way the world ends \\
This is the way the world ends \\
This is the way the world ends \\
Not with a bang but a whimper.

当文本使用一个或多个空格或制表符缩进时,也会强制换行符,这对于排版代码很有用:

The following paragraph has hard line breaks for every line of code:

    import pyglet

    window = pyglet.window.Window()
    pyglet.app.run()

可以使用属性标记设置文本样式:

This sentence makes a {bold True}bold{bold False} statement.

属性标签由属性名称组成(在本例中, bold )后跟一个Python bool、int、浮点数、字符串、元组或列表。

与大多数结构化文档(如HTML)不同,属性文本没有样式“结束”的概念;样式只是在文档中更改。这完全对应于 FormattedDocument 在内部。

下面是更多的例子:

{font_name 'Times New Roman'}{font_size 28}Hello{font_size 12},
{color (255, 0, 0, 255)}world{color (0, 0, 0, 255)}!

(本例使用28pt Times New Roman表示单词“Hello”,使用12pt红色文本表示单词“world”)。

可以通过在样式名称前加上句点(.)来设置段落样式。这可确保样式范围完全包含段落:

{.margin_left "12px"}This is a block quote, as the margin is inset.

{.margin_left "24px"}This paragraph is inset yet again.

属性文本可以作为Unicode字符串加载。此外,任何字符都可以插入,给出其Unicode码位的数字形式,可以是十进制的:

This text is Copyright {#169}.

或十六进制:

This text is Copyright {#xa9}.

这些人物 {} 可以通过复制它们来转义:

Attributed text uses many "{{" and "}}" characters.

使用 decode_attributed 函数将属性文本解码为 FormattedDocument **

document = pyglet.text.decode_attributed('Hello, {bold True}world')

HTML

虽然属性文本允许访问的所有功能 FormattedDocumentTextLayout() ,它相当冗长,很难在中产生文本。为了方便起见,pyglet提供了一个HTML4.01解码器,它可以将一小部分常用的HTML子集转换成 FormattedDocument

请注意,解码器不会保留HTML文档的结构--元素层次结构的所有概念在转换过程中都会丢失,只保留可见的样式更改。

下面的示例使用 decode_html() 要创建 FormattedDocument 从一串HTML::

document = pyglet.text.decode_html('Hello, <b>world</b>')

支持以下元素:

B BLOCKQUOTE BR CENTER CODE DD DIR DL EM FONT H1 H2 H3 H4 H5 H6 I IMG KBD
LI MENU OL P PRE Q SAMP STRONG SUB SUP TT U UL VAR

这个 style 属性不受支持,因此字体大小必须指定为介于1到7之间的HTML逻辑大小,而不是磅大小。可以通过子类化来修改相应的字体大小和其他一些样式表参数 HTMLDecoder

自定义元素

图形和其他可视元素可以使用内联方式插入到文档中 insert_element() 。例如,内联元素用于呈现包含在 IMG 标签。目前不支持浮动或绝对定位的元素。

元素必须子类 InlineElement 并重写 placeremove 方法:研究方法。这些方法由 TextLayout() 当元素变为可见或不再可见时。为 TextLayout()ScrollableTextLayout ,这是在文档中添加或移除元素时;但对于 IncrementalTextLayout 当元素滚动进出视区时,也会调用这些方法。

的构造函数 InlineElement 给出元素的宽度和高度(分为基线上方的坡度和基线以下的坡度)。

通常情况下, InlineElement 子类将图形基元添加到布局的图形批次中;尽管应用程序可能会选择简单地记录元素的位置并单独渲染它。

元素在文档文本中的位置用NUL字符(U+0000)占位符标记。这样做的效果是,将元素插入到文档中会使文档文本的长度增加1。元素也可以像普通字符文本一样设置样式,尽管布局会忽略任何此类样式属性。

用户可编辑的文本

虽然pyglet没有为应用程序提供任何完整的图形用户界面小部件,但它确实实现了实现交互式文本编辑所需的许多功能。这些可用作更完整的图形用户界面系统的基础,或显示一个简单的文本输入字段,如 examples/text_input.py 举个例子。

IncrementalTextLayout 应始终用于可由用户编辑的文本。此类维护有关字形在屏幕上放置的信息,因此可以将窗口坐标映射到文档位置,反之亦然。这些方法是 get_position_from_point()get_point_from_position()get_line_from_point()get_point_from_line()get_line_from_position()get_position_from_line()get_position_on_line()get_line_count()

可以使用文档位置来调整文档的可视矩形,而不是使用 ensure_line_visible()ensure_x_visible() 方法:研究方法。

IncrementalTextLayout 可以通过临时覆盖所选文本的前景色和背景色来显示当前所选文本。这个 selection_startselection_end 属性提供所选内容的范围, selection_colorselection_background_color 要使用的颜色(默认为蓝色上的白色)。

这个 Caret 类为实现插入插入符号(光标) IncrementalTextLayout 。这包括在正确的位置显示闪烁的插入符号,以及处理键盘、文本和鼠标事件。响应事件的行为与Windows、Mac OS X和GTK上的系统图形用户界面非常相似。vbl.使用 Caret 将您从使用 IncrementalTextLayout 方法直接在上面描述。

下面的示例创建一个文档、一个布局和一个插入符号,并将插入符号附加到窗口以侦听事件:

import pyglet

window = pyglet.window.Window()
document = pyglet.text.document.FormattedDocument()
layout = pyglet.text.layout.IncrementalTextLayout(document, width, height)
caret = pyglet.text.caret.Caret(layout)
window.push_handlers(caret)

当绘制布局时,插入符号也将被绘制,因此这个示例几乎已经足够完整,可以显示用户输入。但是,当窗口中只有一个可编辑文本布局时,它适合使用。如果要显示多个文本小部件,则需要某种机制将事件调度到具有键盘焦点的小部件。中给出了一个如何执行此操作的示例 examples/text_input.py 示例程序。

正在加载系统字体

Layout类根据需要自动加载字体。您还可以显式加载字体以实现您自己的布局算法。

要加载字体,您必须知道其系列名称。这是在任何应用程序的字体对话框中显示的名称。例如,所有操作系统都包括 Times New Roman 字体。您还必须指定要加载的字体大小,以磅为单位:

# Load "Times New Roman" at 16pt
times = pyglet.font.load('Times New Roman', 16)

字体的粗体和斜体变体可以使用关键字参数指定::

times_bold = pyglet.font.load('Times New Roman', 16, bold=True)
times_italic = pyglet.font.load('Times New Roman', 16, italic=True)
times_bold_italic = pyglet.font.load('Times New Roman', 16,
                                     bold=True, italic=True)

为了在所有平台上实现最大兼容性,您可以按首选顺序指定要加载的字体名称列表。例如,许多用户都安装了Microsoft Web字体包,其中包括 Verdana ,但不能保证这一点,因此您可以指定 ArialHelvetica 作为适当的替代方案:

sans_serif = pyglet.font.load(('Verdana', 'Helvetica', 'Arial'), 16)

此外,您还可以使用以下命令检查字体的可用性 pyglet.font.have_font() **

# Will return True
pyglet.font.have_font('Times New Roman')

# Will return False
pyglet.font.have_font('missing-font-name')

如果您并不特别关心使用哪种字体,而只需要显示一些可读文本,则可以指定 None 作为族名称,将加载默认的sans-serif字体(在Mac OS X上为Helvetica,在Windows XP上为Arial):

sans_serif = pyglet.font.load(None, 16)

字体大小

加载字体时,必须指定要呈现的字体大小(以磅为单位)。点是一种在显示和印刷媒体中都使用的有点历史但又很传统的单位。对于一个点的实际长度有各种不同的定义,但pyglet使用的是PostScript定义:1点=1/72英寸。

字体分辨率

屏幕上字体的实际呈现大小取决于显示分辨率。在所有操作系统上,Pyglet使用默认DPI 96。大多数Mac OS X应用程序使用的DPI为72,因此字体大小在该操作系统上不会匹配。然而,应用程序开发人员可以放心,在不同平台上保持相同的字体大小。

DPI可以直接在 pyglet.font.load() 函数,并作为 TextLayout() 构造函数。

确定字体大小

加载特定大小的字体后,可以使用以下属性查询其像素大小::

Font.ascent
Font.descent

这些测量结果如下图所示。

../_images/font_metrics.png

字体度量。请注意,下降通常是负的,因为它下降到基线以下。

您可以按以下公式计算连续文本行之间的距离:

ascent - descent + leading

哪里 leading 是要在每行文本之间插入的像素数。

加载自定义字体

如果您的应用程序不是通常安装在目标平台上,您可以为其提供一种字体。您应该确保您拥有分发字体的许可证--术语通常在字体文件本身中指定,并且可以使用操作系统的字体查看器查看。

加载自定义字体必须分两步执行:

  1. 让pyglet知道额外的字体或字体文件。

  2. 按其系列名称加载字体。

例如,假设您有 Action Man 名为的文件中的字体 action_man.ttf 。下面的代码将加载该字体的一个实例:

pyglet.font.add_file('action_man.ttf')
action_man = pyglet.font.load('Action Man')

同样,添加字体文件后,可以将字体名称指定为标签或布局上的样式:

label = pyglet.text.Label('Hello', font_name='Action Man')

字体通常分布在每个变体的单独文件中。 Action Man Bold 可能会作为一个单独的文件分发,称为 action_man_bold.ttf ;您也需要让pyglet知道这一点::

font.add_file('action_man_bold.ttf')
action_man_bold = font.load('Action Man', bold=True)

请注意,即使您知道要加载的字体的文件名,也必须将字体的系列名称指定为 pyglet.font.load()

无需磁盘上的文件即可将其添加到pyglet;您可以指定任何类似文件的对象来支持 read 方法。这对于从资源存档或通过网络提取字体非常有用。

如果自定义字体随应用程序一起分发,请考虑使用 应用程序资源

支持的字体格式

Pyglet可以加载操作系统本机支持的任何字体文件,但不是所有格式都完全支持。

下表显示了支持的格式列表。

字体格式

窗口

Mac OS X

Linux(自由类型)

TrueType(.ttf)

X

X

X

PostSCRIPT类型1(.pfm、.pfb)

X

X

X

Windows位图(.fnt)

X

X

Mac OS X数据分叉字体(.dfont)

X

OpenType(.otf) [1]

X

X11字体格式PCF、BDF、SFONT

X

比特流PFR(.pfr)

X

在互联网上发现的一些字体可能遗漏了某些操作系统的信息,其他字体可能是使用不完全符合标准的正在进行的工作工具编写的。在文本编辑器或字体中使用字体,查看者可以帮助确定字体是否损坏。

OpenGL字体注意事项

Piglet中的文本使用带纹理的四边形绘制。每种字体维护一组一个或多个纹理,字形在需要时被上载到其中。对于大多数应用程序,这一细节是透明的,并不重要,但是这些字形纹理的一些细节将在下面为高级用户描述。

上下文亲和力

加载字体时,它会立即在当前上下文的对象空间中创建纹理。如果第一个纹理上没有足够的空间容纳所有字形,则可能需要创建后续纹理。这是在第一次请求字形时完成的。

在执行任何纹理操作时,Piglet始终假定加载字体时处于活动状态的对象空间为活动对象空间。通常情况下,这一假设是有效的,因为缺省情况下,pyglet在所有上下文之间共享对象空间。不过,在少数情况下,情况并非如此:

  • 在上下文创建期间显式设置上下文共享时。

  • 当使用不支持共享上下文对象空间的多个显示设备时。

在上述任何一种情况下,您都需要为所需的每个对象空间重新加载字体。Pyglet保留一个字体缓存,但它是按对象空间缓存字体的,因此它知道何时可以重用现有字体实例,或者是否需要加载该实例并创建新纹理。当可能需要添加任何字形时,您还需要确保适当的上下文处于活动状态。

混合状态

字形纹理的内部格式为 GL_ALPHA ,它提供了一种通过更改顶点颜色来重新着色和混合抗锯齿文本的简单方法。Piglet对OpenGL状态的假设很少,除了更改当前绑定的纹理外,不会更改它。

以下混合状态用于绘制字体字形:

from pyglet.gl import *
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable(GL_BLEND)

所有字形纹理都使用 GL_TEXTURE_2D 目标,因此您应该确保优先级更高的目标(如 GL_TEXTURE_3D 在尝试呈现文本之前未启用。