跳转至

pl-graph元件

使用 PyGraphviz 库创建 Graphviz DOT 可视化。

样本元素

使用 graphviz 语法的 pl-graph 元素的屏幕截图

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

使用矩阵的 pl-graph 元素的屏幕截图

question.html
<pl-graph params-name="matrix" params-name-labels="labels"></pl-graph>
server.py
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)

question.html
<pl-graph params-type="networkx" params-name="random-graph"></pl-graph>
server.py
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-matrixnetworkx,但可以通过扩展添加自定义类型。
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

对于静态图,直接描述它:

question.html
<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>

对于随机生成的图形,像“随机图形”这样的固定字符串不会告诉屏幕阅读器用户实际绘制的内容。从相同的数据生成描述,使其始终与图表匹配:

server.py
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}."
    )
question.html
<pl-graph params-type="networkx" params-name="graph" aria-label="{{ params.graph_alt }}"></pl-graph>

以可访问的格式呈现相同的信息

将大图打包到单个 aria-label 字符串中会产生长的、非结构化的公告,难以导航。通常更好的选择是以每个学生都受益的本机可访问格式呈现数据,例如使用 pl-matrix-latex 渲染的邻接矩阵或边表:

question.html
<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 的自定义值。每个自定义类型都定义为一个函数,该函数将 elementdata 值作为输入,并返回处理后的 DOT 语法作为输出。

最小类型函数可能类似于:

def custom_type(element, data):
    return "graph { a -- b; }"

为了注册这些自定义类型,您的扩展应该定义全局 backends 字典。这会将 params-type 的值映射到上面的函数:

backends = {
    'my-custom-type': custom_type
}

当扩展被导入时,这将被自动获取。如果您的扩展需要定义额外的属性,您可以选择定义全局 optional_attribs 数组,其中包含元素可以使用的属性列表。

要获得完整的实现,请查看示例课程中的 edge-inc-matrix 扩展。

参见