【问题标题】:How can I create named edge "types" in Graphviz/dot/neato?如何在 Graphviz/dot/neato 中创建命名边缘“类型”?
【发布时间】:2021-11-19 00:54:12
【问题描述】:

我需要用 graphviz/dot 绘制一个图表,其中节点之间有共同的边缘类型,我试图找到一种方法来为每种类型的边缘定义一个标签,然后在图中多次使用该标签。

例如,想象一下传统的吊扇 FSM 示例,它最初处于关闭状态,每次有人拉动电源线时,它都会根据风扇的速度更改为新状态:

     Pull         Pull        Pull
OFF ------> HIGH ------> MED ------> LOW
 ^                                    |
 |                Pull                |
 +------------------------------------+

每条边都被命名为“Pull”,我可以使用以下方法在点中定义它:

digraph fan {
    OFF  -> HIGH [label="Pull"];
    HIGH -> MED  [label="Pull"];
    MED  -> LOW  [label="Pull"];
    LOW  -> OFF  [label="Pull"];
}

但我不想每次都指定相同的文本标签,因为

  1. 我的标签可能会变得很长,因此很容易出错,并且
  2. 我的边缘除了标签之外还有其他属性,例如颜色,以及
  3. 我有多种不同类型的边可供选择,因此我想确保图表中不同上下文中使用的边类型“A”始终具有所有相同的属性。

我希望 dot 有一种语法,可以让我为边缘类型定义名称,例如:

digraph fan {
    edge_a [label="Pull"];

    OFF  -> HIGH edge_a;
    HIGH -> MED  edge_a;
    MED  -> LOW  edge_a;
    LOW  -> OFF  edge_a;
}

当然,真正做的是创建一个名为“Pull”的节点和未标记的边。

我已经在网上搜索了几个小时,但没有成功。有人知道如何预先定义边缘类型以在多个位置使用吗?

更新: @vaettchen had suggested 定义一个边缘类型,然后列出该边缘类型的所有转换,然后定义下一个边缘类型,然后是它的转换。虽然这在技术上可以解决我的问题,但它会引入其他几个问题,因为我今天的图表可能看起来像:

digraph {
    subgraph cluster_1 {
        a -> b [label="type x", color=red, style=solid];
        b -> a [label="type y", color=green, style=dashed];

        b -> c [label="type x", color=red, style=solid];
        c -> b [label="type y", color=green, style=dashed];

        c -> d [label="type z", color=blue, style=dotted];
    }

    subgraph cluster_2 {
        d -> e [label="type x", color=red, style=solid];
        e -> d [label="type y", color=green, style=dashed];

        e -> f [label="type x", color=red, style=solid];
        f -> e [label="type y", color=green, style=dashed];

        f -> c [label="type z", color=blue, style=dotted];
    }
}

并且要通过边缘类型重新排列,我会在让双向边缘彼此相邻(a->b 和 b->a)的代码中失去直接的视觉清晰度,我必须明确列出每个子图中的节点,我必须将子图内部的边定义拉到主图中:

digraph {
    edge [label="type x", color=red, style=solid];
    a -> b;
    b -> c;
    d -> e;
    e -> f;

    edge [label="type y", color=green, style=dashed];
    b -> a;
    c -> b;
    e -> d;
    f -> e;

    edge [label="type z", color=blue, style=dotted];
    c -> d;
    f -> c;

    subgraph cluster_1 {
        a; b; c;
    }

    subgraph cluster_2 {
        d; e; f;
    }
}

因此,虽然它可以解决我提出的问题并且我很欣赏这个建议,但我不确定它是否值得权衡,因为你最终得到了一个 C 程序,你必须在其中定义所有变量函数并按其类型而不是逻辑关联来组织它们。

为了清楚起见,如果存在这样的“edge_type”定义关键字,那么在上面的示例中,我真正希望的结果如下所示:

digraph {
    edge_type edge_x [label="type x", color=red, style=solid];
    edge_type edge_y [label="type y", color=green, style=dashed];
    edge_type edge_z [label="type z", color=blue, style=dotted];

    subgraph cluster_1 {
        a -> b edge_x;
        b -> a edge_y;

        b -> c edge_x;
        c -> b edge_y;

        c -> d edge_z;
    }

    subgraph cluster_2 {
        d -> e edge_x;
        e -> d edge_y;

        e -> f edge_x;
        f -> e edge_y;

        f -> c edge_z;
    }
}

【问题讨论】:

  • 亲爱的驾车投票者 - 想解释一下原因吗?该问题有示例输入、输出、尝试的解决方案和需求说明,因此不确定我可以做些什么来改进它。
  • 再次感谢,我去看看。谈到 github,我希望我能找到一种方法将 .dot 规范包含在 README.md 中,并在有人在 github 上查看它时自动呈现图形。因此,如果我在 .dot 中有一个系统的文本模型,我也不必手动更新它的图像。我找到了一个站点 (github.com/TLmaK0/gravizo),我可以通过引用我的 .dot 文件链接到该站点,并且该站点确实有效,但是我正在处理的 github 站点是专有的,因此外部站点实际上看不到我的内部 .dot 文件.你遇到过类似的事情吗?
  • 基本上它是一些我可以安装在 github 存储库中的软件,它可以完成 Grazivo 的工作(但现在也可以使用你的 m4 预处理器!)

标签: graphviz dot neato


【解决方案1】:

不是真正的答案,而是“深思熟虑”,因为我认为 graphviz 中不存在命名标签:您可以为以下边缘定义默认标签。如果您的工作流程允许在一个地方定义所有边缘,那将很有效。示例:

digraph rs
{
    node[ shape = box, style = rounded]

    edge[ label = "pull" ];
    { A B } -> C;
    G -> H;
    C -> D[ label = "stop" ];
    edge[ label = "push"];
    D -> { E F };
    edge[ color = red, fontcolor = red ];
    { E F } -> G;
}

产生

我也尝试过用

实现你的图表
digraph fan 
{
    splines = ortho;
    node [ shape=box ]

    edge [ xlabel = "Pull", minlen = 4 ];
    { rank = same; OFF  -> HIGH -> LOW; }
    LOW:s -> OFF:s;
}

产生

所以它看起来不错,但所有的调整都很难扩展。

【讨论】:

  • 感谢您的建议。从技术上讲,我可以按照您的建议来定义边缘特征,然后列出该类型的所有转换,但是现在我在大多数节点之间都有两个方向的边缘(例如 Off->On 和 On->Off),而且我有边缘类型 A 出现在多个子图中的子图,因此我将在每个子图中显式列出节点时添加更多工作,将内部边缘定义从子图中拉出,并且我将失去具有 to/ 的易读性对称性检查来自并置的每对节点的边。
  • 是的,我认为它比粉丝更复杂... -- 你知道gvpr 吗?这对我来说太过分了,但你可能会想办法为你服务。
  • gvpr 很有趣,感谢您的建议。我一直在考虑编写一个 awk 程序来预处理一个带有边缘类型定义的点状文件,如果 dot 本身不支持该文件,但 gvpr 可能是一种更强大的方法。我需要做更多的阅读......
  • 看起来虽然这不是我希望的答案,但它似乎是正确的答案,所以我接受它,再次感谢!
【解决方案2】:

我想我得到了您的解决方案,使用 m4(感谢 Simon)。使用和调整您的示例,我创建了一个名为 gv.m4 的文件:

digraph {
    define(`edge_x',`[label="type x", color=red, style=solid]')
    define(`edge_y',`[label="type y", color=green, style=dashed]')
    define(`edge_z',`[label="type z", color=blue, style=dotted]')

    subgraph cluster_1 {
        a -> b edge_x;
        b -> a edge_y;

        b -> c edge_x;
        c -> b edge_y;

        c -> d edge_z;
    }

    subgraph cluster_2 {
        d -> e edge_x;
        e -> d edge_y;

        e -> f edge_x;
        f -> e edge_y;

        f -> c edge_z;
    }
}

并用简单的命令进行转换

m4 gv.m4 > gv.dot

现在包含您定义的边缘

digraph {

    subgraph cluster_1 {
        a -> b [label="type x", color=red, style=solid];
        b -> a [label="type y", color=green, style=dashed];

        b -> c [label="type x", color=red, style=solid];
        c -> b [label="type y", color=green, style=dashed];

        c -> d [label="type z", color=blue, style=dotted];
    }

    subgraph cluster_2 {
        d -> e [label="type x", color=red, style=solid];
        e -> d [label="type y", color=green, style=dashed];

        e -> f [label="type x", color=red, style=solid];
        f -> e [label="type y", color=green, style=dashed];

        f -> c [label="type z", color=blue, style=dotted];
    }
}

并产生预期的图形:

您可以使用 m4 做更多事情 - graphViz 中缺少的东西,例如维护和(甚至有条件地)包括子文件。例如,如果您将两个子图放入两个单独的文件 gv1.txtgv2.txt,这将很好地工作:

digraph incl
{
    define(`edge_x',`[label="type x", color=red, style=solid]')
    define(`edge_y',`[label="type y", color=green, style=dashed]')
    define(`edge_z',`[label="type z", color=blue, style=dotted]')
    include(gv1.txt)
    include(gv2.txt)
     e -> d[ color = yellow, label = "this is new!"];
}

【讨论】:

  • 很有趣,谢谢。在您的define 行中,您混合使用了反引号和撇号(例如`edge_x' = 反引号边缘撇号) - 这真的是需要使用的语法还是错字?我没有 m4 可用于测试...
  • 这确实是所需的语法。代码是从生成图表的实际测试中复制和粘贴的。
【解决方案3】:

我很难在我的机器上下载 m4,因此选择通过 python API 使用 graphviz,您可以在其中将样式定义为字典并根据需要应用于节点/边。

import graphviz

dot = graphviz.Digraph(comment='Test File')


nodeAttr_statement = dot.node_attr = {"shape": 'box', "style": 'filled', "fillcolor":"red"}
nodeAttr_question = dot.node_attr = {"shape": 'diamond', "style": 'filled', "fillcolor":"blue"}

dot.edge_attr

edge_Attr_sample = dot.edge_attr = {"arrowhead":'vee',"color":"yellow"}
edge_Attr_sample2 = dot.edge_attr = {"arrowhead": 'diamond', "color": "green"}


dot.node("A", "A", nodeAttr_statement)
dot.node("B", "B", nodeAttr_question )



dot.edge("A", "B", _attributes=edge_Attr_sample)
dot.edge("B", "A", _attributes=edge_Attr_sample2)
dot.format = 'pdf'
dot.render('test', view=True)

输出

// Test File
digraph {
    node [fillcolor=blue shape=diamond style=filled]
    edge [arrowhead=diamond color=green]
    A [label=A fillcolor=red shape=box style=filled]
    B [label=B fillcolor=blue shape=diamond style=filled]
    A -> B [arrowhead=vee color=yellow]
    B -> A [arrowhead=diamond color=green]
}

Output image from python script

【讨论】:

    猜你喜欢
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 2012-09-05
    • 1970-01-01
    • 2019-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多