GraphViz - 如何连接子图?

229
GraphVizDOT语言中,我正试图表示一个依赖图。我需要在容器内部放置节点,并能够使节点和/或容器依赖于其他节点和/或容器。我使用subgraph来表示我的容器。节点链接完全没有问题,但我无法弄清如何连接子图。给定以下程序,我需要能够用箭头连接cluster_1cluster_2,但我尝试过的任何内容都会创建新的节点,而不是连接这些集群:
digraph G {

    graph [fontsize=10 fontname="Verdana"];
    node [shape=record fontsize=10 fontname="Verdana"];

    subgraph cluster_0 {
        node [style=filled];
        "Item 1" "Item 2";
        label = "Container A";
        color=blue;
    }

    subgraph cluster_1 {
        node [style=filled];
        "Item 3" "Item 4";
        label = "Container B";
        color=blue;
    }

    subgraph cluster_2 {
        node [style=filled];
        "Item 5" "Item 6";
        label = "Container C";
        color=blue;
    }

    // Renders fine
    "Item 1" -> "Item 2";
    "Item 2" -> "Item 3";

    // Both of these create new nodes
    cluster_1 -> cluster_2;
    "Container A" -> "Container C";
}

这里输入图片描述


3
我有同样的问题,但他们举了一个自然的例子,其中子图像像节点一样起作用,http://www.graphviz.org/content/fdpclust。 - nlucaroni
2
@nlucaroni 我想知道这个问题是否已经解决。这个例子给我错误的图形:边缘连接子图的中心。你不知道如何使它像示例一样工作吗? - k102
1
@k102,我知道。再次查看该页面;它说你需要使用 fdp。链接的示例和上面的示例都有效(此处示例的最后一行需要使用子图名称而不是标签,并且可能很好地包括图形的线长度);它有点紧凑。 - nlucaroni
1
@nlucaroni 使用fdp v2.28.0,并从示例中复制/粘贴源代码后,线条连接到子图的中心而不是边缘。如果您在OmniGraffle中打开.dot,则它们将正确连接,而“neato”和“dot”都会为群集创建多余的节点。 - Phrogz
相关的Graphviz错误报告:https://gitlab.com/graphviz/graphviz/-/issues/745以及从那里链接到的其他报告。 - Peter Nowee
1
FDP集群现已位于https://www.graphviz.org/Gallery/undirected/fdpclust.html。 - Ed.
4个回答

271

DOT用户手册给出了一个带有集群和集群之间边的图形示例:

重要提示:需要初始的compound=true语句。

注意保留原文中的HTML标签。
digraph G {
  compound=true;
  subgraph cluster0 {
    a -> b;
    a -> c;
    b -> d;
    c -> d;
  }
  subgraph cluster1 {
    e -> g;
    e -> f;
  }
  b -> f [lhead=cluster1];
  d -> e;
  c -> g [ltail=cluster0,lhead=cluster1];
  c -> e [ltail=cluster0];
  d -> h;
}

...和节点与群集之间的边缘:

这里输入图片描述


26
谢谢 - 这个方法可行,但是感觉有一点丑陋。我希望不会出现容器没有节点的情况。 - Winston Smith
6
如果有人感兴趣的话,这可能会导致位置问题,特别是在您标记链接(边缘)时。即使边缘的头部或尾部被隐藏在簇下面,标签仍然位于中点位置,这意味着一些边缘标签看起来似乎漂浮在簇上方,而不是由边缘自身定位。 - Winston Smith
83
老问题了,但我曾经遇到过类似的问题,并通过每个簇添加一个不可见的虚拟节点来解决它,即使簇为空,该虚拟节点也可以被连接。DUMMY_0 [shape=point style=invis] - DevSolar
3
当使用仅垂直连接的聚类时,我发现聚类之间的边会被压缩成箭头。我通过在边上使用minlen=1来解决这个问题。c -> g [ltail=cluster0, lhead=cluster1, minlen=1]; - Freenerd
4
这是手册的链接,其中包含示例:http://graphviz.org/Documentation/dotguide.pdf(第30页)。 - Kirill Bulygin
显示剩余5条评论

111

为了方便参考,应用于原问题的HighPerformanceMark答案描述如下:

digraph G {

    graph [fontsize=10 fontname="Verdana" compound=true];
    node [shape=record fontsize=10 fontname="Verdana"];

    subgraph cluster_0 {
        node [style=filled];
        "Item 1" "Item 2";
        label = "Container A";
        color=blue;
    }

    subgraph cluster_1 {
        node [style=filled];
        "Item 3" "Item 4";
        label = "Container B";
        color=blue;
    }

    subgraph cluster_2 {
        node [style=filled];
        "Item 5" "Item 6";
        label = "Container C";
        color=blue;
    }

    // Edges between nodes render fine
    "Item 1" -> "Item 2";
    "Item 2" -> "Item 3";

    // Edges that directly connect one cluster to another
    "Item 1" -> "Item 3" [ltail=cluster_0 lhead=cluster_1];
    "Item 1" -> "Item 5" [ltail=cluster_0 lhead=cluster_2];
}

关键是:

  • graph 声明中使用 compound=true
  • 为子图命名为 cluster_*
  • 根据下面的评论,不要以大写字母开头命名集群名称。 我不确定这意味着什么(例如,是否禁止cluster_A?),但希望在此强调一下,以防它成为问题。

这将产生输出:

graph with connected clusters

请注意,我更改了边缘以引用集群内部的节点,为每个边缘添加了ltail和lhead属性,指定了集群名称,并添加了图级属性“ compound = true”。

关于担心可能想要连接没有节点的集群的问题,我的解决方案(未在上面显示)是始终向每个集群添加一个节点,以style=plaintext渲染。使用此节点标记集群(而不是集群的内置“ label”属性,该属性应设置为空字符串(在Python中,label='""')。 这意味着我不再添加直接连接集群的边缘,但在我特定的情况下有效。


25
注意:'graph [fontsize=10 fontname="Verdana" compound=true];' 是必不可少的 - 如果你漏掉了它,链接到ltail/lhead就不起作用。 - s.Daniel
1
@JonathanHartley,根据您最后一段的意思,有没有办法将该节点居中放置在集群的正中间? - Pacerier
1
集群的名称也不应以大写字母开头。 - JCLL
8
只需要添加 "compound=true" 就可以了。 - Dr. Max Völkel
不要在链接“Item 1”->“Item 3”时重置lhead和ltail,而是如何用有意义的代码将cluster_0和cluster_1链接起来?我的意思是,使cluster_0 -> cluster_1呈现为您的输出。因为cluster_0中可能有许多项链接到cluster_1中的其他许多项(多对多或一对多)。最好只链接两个。 - Mithril
@Mithral 我同意这会很好。我不是专家,但据我所知,它不能直接在DOT中完成。我认为生成您的DOT文件的任何内容都需要看到有许多这样的链接,并将它们归结为DOT文件中的一个链接,选择簇内的任意两个节点。如果生成DOT文件的东西是您自己,那么这就是您的工作。 :-( - Jonathan Hartley

15

确保你正在使用fdp布局文件。我认为neato不支持簇。


5
我也有实际经验表明 neato 引擎不支持簇集。我不确定这是否是一个错误。 - Ross Rogers
看起来这不再是事实。 - Dzamo Norton

1

请确保在有向图选项(参考文献)中设置compound=true:

digraph {
  compound=true;

  subgraph cluster_a {
    label="Cluster A";
    node1; node3; node5; node7;
  }
  subgraph cluster_b {
    label="Cluster B";
    node2; node4; node6; node8;
  }

  node1 -> node2 [label="1"];
  node3 -> node4 [label="2" ltail="cluster_a"];
  
  node5 -> node6 [label="3" lhead="cluster_b"];
  node7 -> node8 [label="4" ltail="cluster_a" lhead="cluster_b"];
}

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接