pl-graph元件¶
使用 PyGraphviz 库创建 Graphviz DOT 可视化。
样本元素¶

<pl-graph> digraph G { A -> B } </pl-graph>

<pl-graph params-name="matrix" params-name-labels="labels"></pl-graph>
import prairielearn as pl
import numpy as np
def generate(data):
mat = np.random.random((3, 3))
mat = mat / np.linalg.norm(mat, 1, axis=0)
data["params"]["labels"] = pl.to_json(["A", "B", "C"])
data["params"]["matrix"] = pl.to_json(mat)
<pl-graph params-type="networkx" params-name="random-graph"></pl-graph>
import random
import string
import prairielearn as pl
import networkx as nx
def generate(data):
random_graph = nx.gnm_random_graph(5, 6)
for in_node, out_node, edge_data in random_graph.edges(data=True):
edge_data["label"] = random.choice(string.ascii_lowercase)
data["params"]["random-graph"] = pl.to_json(random_graph)
定制¶
| 属性 | 类型 | 默认 | 描述 |
|---|---|---|---|
aria-label |
字符串 | — | 描述图表的简短文本替代方案,由屏幕阅读器阅读。请参阅辅助功能部分。 |
aria-description |
字符串 | — | 由屏幕阅读器阅读的更长、更详细的图表文本描述。需要 aria-label。请参阅辅助功能部分。 |
directed |
布尔 | 真实 | 是否将邻接矩阵中的边视为有向或无向。如果设置为 false,则边缘将呈现为无向。 如果设置为 false,则输入邻接矩阵必须是对称的。 |
directory |
字符串 | "." |
源文件所在目录。可以是 "."(问题目录)、"clientFilesCourse" 或 "serverFilesCourse"。 |
engine |
字符串 | 点 | 使用的渲染引擎;支持 "circo"、"dot"、"fdp"、"neato"、"osage" 和 "twopi"。 |
log-warnings |
布尔 | 真实 | 是否记录 Graphviz 渲染期间发生的警告。 |
negative-weights |
布尔 | 假 | 是否识别邻接矩阵中的负权重。如果设置为 false,则忽略最多为 0 的所有权重(不计为边缘)。如果设置为 true,则识别所有非 None 的权重。 |
params-name |
字符串 | — | 包含用作输入的数据的参数名称。要使用的数据类型取决于 params-type 属性。 |
params-name-labels |
字符串 | — | 使用邻接矩阵时,包含每个节点标签的参数。 |
params-type |
字符串 | "adjacency-matrix" |
使用哪个后端从数据渲染图形。默认情况下,仅存在 adjacency-matrix 和 networkx,但可以通过扩展添加自定义类型。 |
source-file-name |
字符串 | — | 要从中加载图形内容的文件的名称。如果提供,将使用文件内容而不是元素的内部 HTML。对于具有特殊字符(例如基于记录的节点中的尖括号)的复杂图形很有用。 |
weights |
布尔 | — | 使用邻接矩阵时,是否显示边权重。默认情况下,将自动显示随机矩阵的权重(当它们不是二进制 0/1 时)。 |
weights-digits |
整数 | 2 | 使用邻接矩阵时,显示权重的位数。 |
weights-presentation-type |
字符串 | "f" |
使用邻接矩阵时权重的数字显示格式。如果 weights-presentation-type 为 "sigfig",则使用 to_precision 模块将每个数字格式化为有效数字。否则,每个数字的格式为 {:.{digits}{presentation-type}}。 |
从已弃用的属性迁移¶
为了向后兼容,仍支持以下已弃用的属性:
| 旧语法 | 新语法 |
|---|---|
params-name-matrix="<name>" |
params-name="<name>" |
细节¶
请注意,使用 networkx 进行渲染,在创建 Graphviz DOT 可视化时,将保留输入 networkx 图形中的属性。因此,可以将节点和边属性(例如颜色、线宽)设置为输入图的一部分,并将这些属性反映在渲染中。其中包括图形的全局属性,例如渲染中使用的 rankdir。有关支持哪些属性的更多信息,请参阅有关属性的 Graphviz 文档。目前使用的Graphviz版本是2.44.0。
当处理包含特殊字符(如尖括号 (<>))的静态图时,source-file-name 属性特别有用,这些特殊字符用在 基于记录的节点 中,但可能会干扰 HTML 解析。通过将图形内容放置在外部文件中,您可以避免转义这些字符。
无障碍¶
pl-graph 呈现为 SVG。 Graphviz 向每个节点和边添加了 <title>,但这些是悬停工具提示,而不是整个图的描述,因此屏幕阅读器用户默认情况下不会获得任何有意义的信息。如何提供可访问的等效项取决于图表的使用方式。
信息图¶
如果图表传达了信息(而不是学生必须解释才能回答的内容),请用 aria-label 为其提供替代文本。设置 aria-label 后,图形将作为单个标记图像暴露给辅助技术,这也隐藏了 Graphviz 的每个节点标题。如果需要,请使用 aria-description 获取其他详细信息,但 aria-description 属性具有不一致的屏幕阅读器支持并且还需要设置 aria-label。
对于静态图,直接描述它:
<pl-graph aria-label="Directed graph with an edge from A to B and from B to C">
digraph G { A -> B -> C }
</pl-graph>
对于随机生成的图形,像“随机图形”这样的固定字符串不会告诉屏幕阅读器用户实际绘制的内容。从相同的数据生成描述,使其始终与图表匹配:
import prairielearn as pl
import networkx as nx
def generate(data):
graph = nx.gnm_random_graph(5, 6)
data["params"]["graph"] = pl.to_json(graph)
edges = ", ".join(f"{u} to {v}" for u, v in graph.edges())
data["params"]["graph_alt"] = (
f"Undirected graph with {graph.number_of_nodes()} nodes "
f"and {graph.number_of_edges()} edges: {edges}."
)
<pl-graph params-type="networkx" params-name="graph" aria-label="{{ params.graph_alt }}"></pl-graph>
以可访问的格式呈现相同的信息¶
将大图打包到单个 aria-label 字符串中会产生长的、非结构化的公告,难以导航。通常更好的选择是以每个学生都受益的本机可访问格式呈现数据,例如使用 pl-matrix-latex 渲染的邻接矩阵或边表:
<pl-graph
params-type="networkx"
params-name="graph"
aria-label="Undirected graph; its edges are listed in the table below"
></pl-graph>
<table>
<caption>
Edges of the graph above
</caption>
<thead>
<tr>
<th scope="col">From</th>
<th scope="col">To</th>
</tr>
</thead>
<tbody>
{{#params.edge_rows}}
<tr>
<td>{{from}}</td>
<td>{{to}}</td>
</tr>
{{/params.edge_rows}}
</tbody>
</table>
图表就是问题所在¶
如果学生必须阅读图表才能回答(例如,“给出下图的邻接矩阵”),则描述性的 aria-label 不合适。它出现在页面源代码中,因此它会揭示答案,并且没有任何文本替代方案可以以学生期望推理的形式传达图表。屏幕阅读器用户无法按原样访问此类问题;请提供问题的替代版本。
示例实现¶
- [元素/图]
扩展API¶
可以使用元素扩展 添加 params-type 的自定义值。每个自定义类型都定义为一个函数,该函数将 element 和 data 值作为输入,并返回处理后的 DOT 语法作为输出。
最小类型函数可能类似于:
def custom_type(element, data):
return "graph { a -- b; }"
为了注册这些自定义类型,您的扩展应该定义全局 backends 字典。这会将 params-type 的值映射到上面的函数:
backends = {
'my-custom-type': custom_type
}
当扩展被导入时,这将被自动获取。如果您的扩展需要定义额外的属性,您可以选择定义全局 optional_attribs 数组,其中包含元素可以使用的属性列表。
要获得完整的实现,请查看示例课程中的 edge-inc-matrix 扩展。