如何在 Graphviz/dot/neato 中创建命名边“类型”?

问题描述 投票:0回答:3

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

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

     Pull         Pull        Pull
OFF ------> HIGH ------> MED ------> LOW
 ^                                    |
 |                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建议定义一个边缘类型,然后列出该边缘类型的所有转换,然后定义下一个边缘类型,然后定义它的转换。虽然这在技术上可以解决我的问题,但它会引入其他几个问题,因为我今天的图表看起来像:

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;
    }
}
graphviz dot neato
3个回答
9
投票

我想我使用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];
    }
}

并产生预期的图表:

enter image description here

您可以使用 m4 做更多事情 - graphViz 中缺少的东西,例如维护和(甚至有条件地)包含子文件。例如,如果您将两个子图放入两个单独的文件

gv1.txt
gv2.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!"];
}

enter image description here


8
投票

不是真正的答案,而是“深思熟虑”,因为我认为

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;
}

产生

enter image description here

我也尝试用

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

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

产生

enter image description here

所以它看起来不错,但经过所有调整后很难扩展。


1
投票

我费尽心思在我的机器上下载 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]
}

Python 脚本输出图像:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.