GraphViz - Как подключить подграфы?

166

На DOTязыке для GraphVizя пытаюсь представить диаграмму зависимостей. Мне нужно иметь возможность иметь узлы внутри контейнера и иметь возможность сделать узлы и / или контейнеры зависимыми от других узлов и / или контейнеров.

Я использую subgraphдля представления своих контейнеров. Связывание узлов работает просто отлично, но я не могу понять, как соединить подграфы.

Учитывая приведенную ниже программу, мне нужно иметь возможность подключаться cluster_1и cluster_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";
}

введите описание изображения здесь

Уинстон Смит
источник
2
У меня та же проблема, но у них есть естественный пример, когда подграфы действуют как узлы, graphviz.org/content/fdpclust .
nlucaroni
1
@nlucaroni Интересно, решена ли эта проблема? этот пример дает мне неправильный график: ребра соединяют центры подграфа. Разве вы не знаете, как заставить это работать, как в примере?
k102
1
@ k102, я знаю. Проверьте эту страницу еще раз; он говорит, что вам нужно использовать fdp. Связанный пример и вышеприведенный пример работают (последняя строка в примере здесь должна использовать имена подграфов, а не метку, и было бы неплохо включить длины строк для графа); это немного туго, как есть).
Nlucaroni
1
@nlucaroni Используя fdpv2.28.0 и скопируйте / вставьте источник из примера, линии соединяются с центром подграфа, а не с краями. Если вы откроете .dot в OmniGraffle, они будут правильно подключены, neatoи dotоба будут создавать лишние узлы для кластера.
Фрогз

Ответы:

190

В руководстве пользователя DOT приведен следующий пример графика с кластерами с ребрами между кластерами:

ВАЖНО: compound=trueТребуется первоначальное утверждение.

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

... и ребра между узлами и кластерами:

введите описание изображения здесь

Высокая производительность
источник
14
Спасибо - это работает, но это действительно похоже на уродливый хак. Я надеюсь, что у меня нет сценария, где у меня есть контейнер без узлов.
Уинстон Смит
5
Если кому-то это интересно, это может вызвать проблемы с позиционированием, если вы пометили ссылки (ребра). Хотя головка или хвост ребра могут быть скрыты под кластером, метка по-прежнему расположена в средней точке, что означает, что некоторые метки ребра кажутся плавающими над кластером, а не позиционируются самим ребром.
Уинстон Смит
58
@WinstonSmith: Старый вопрос, но у меня была похожая проблема, и я решил ее с помощью невидимого фиктивного узла на кластер, который можно связать, даже если в противном случае кластер пуст. DUMMY_0 [shape=point style=invis]
DevSolar
2
Я обнаружил, что мои межкластерные ребра сворачиваются до головок стрелок, когда используются кластеры, которые соединены только вертикально. Я исправил это с minlen = 1 по краям. c -> g [ltail = cluster0, lhead = cluster1, minlen = 1];
Freenerd
3
Вот ссылка на руководство с примером: graphviz.org/Documentation/dotguide.pdf (стр. 30).
Кирилл Булыгин
90

Для удобства пользования решение, описанное в ответе 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];
}

compound=trueВ graphдекларации имеет жизненно важное значение. Это производит вывод:

граф со связанными кластерами

Обратите внимание, что я изменил ребра для ссылки на узлы в кластере, добавил атрибуты ltail и lhead для каждого ребра, указав имя кластера, и добавил атрибут уровня графа 'component = true'.

Что касается опасений, что кто-то может захотеть подключить кластер без узлов внутри него, мое решение состояло в том, чтобы всегда добавлять узел в каждый кластер с помощью style = plaintext. Используйте этот узел для маркировки кластера (вместо встроенного в кластер атрибута «label», который должен быть установлен на пустую строку (в Python, label='""'). Это означает, что я больше не добавляю ребра, которые соединяют кластеры напрямую, но это работает в моей конкретной ситуации.

Джонатан Хартли
источник
24
Примечание: 'graph [fontsize = 10 fontname = "Verdana" component = true]; " важно - если вы пропустите, ссылка на ltail / lhead не работает.
s.Daniel
1
@JonathanHartley, согласно вашему последнему абзацу, есть ли способ центрировать этот узел прямо в середине кластера?
Пейсер
также название кластера не должно начинаться с заглавной буквы
JCLL
7
@ s.Daniel Это просто соединение = правда; что требуется
д-р Макс Фелькель
Вместо сброса lhead и ltail при ссылке «Item 1» -> «Item 3», как связать cluster_0 и cluster_1 со значимым кодом? Я meam, сделать cluster_0 -> cluster_1подарок, как вы выходной. Потому что в cluster_0 может быть много элементов, ссылающихся на множество других элементов в cluster_1 (многие ко многим или от одного ко многим). Было бы хорошо просто связать два.
Мифрил
11

Убедитесь, что вы используете fdpмакет для файла. Я не думаю, neatoподдерживает кластеры.

mihajlv
источник
2
Я тоже на опыте обнаружил, что neatoдвигатель не поддерживает кластеры .. Я не уверен, если это ошибка или нет ..
Росс Роджерс