Итак, рендеринг графиков - это особый гений graphviz , который также имеет несколько библиотек, которые обеспечивают привязки к Python. На мой взгляд, лучшая из этих библиотек привязок - pygraphviz . Graphviz, вероятно, лучшее решение, а также, вероятно, самое простое.
Конкретный макет, который вы описываете в своем Вопросе, иерархическая многоуровневая схема, выполняется без усилий с помощью graphviz ' dot движок макетов. Dot выполняет рендеринг, чтобы гарантировать, что граф размещен в конфигурации естественного дерева - то есть родительские узлы расположены над их дочерними элементами; узлы одинакового ранга (уровни от корня) располагаются на равной оси w / r / t по оси y, когда это возможно, и естественная симметрия сохраняется, когда это возможно.
(Примечание: смущает, точка относится к одному из нескольких механизмов компоновки, которые включают graphviz , но точка также является именем и расширением файла формат для всех документов графика, независимо от того, как они отображаются).
Как вы можете видеть в следующем коде, используя pygraphviz, просто выбрать dot в качестве механизма разметки для вашего графика, хотя на самом деле это не значение по умолчанию ( neato is) .
Вот краткий график, который я сделал и затем отрисовал, используя dot - создавал и рендерил, используя graphviz через pygraphviz .
Обратите внимание, что график имеет идеальное расположение - узлы одинаковой степени находятся на одном уровне вдоль вертикальной оси, дочерние элементы отображаются ниже родителей, и естественная «симметрия» сохраняется, когда это возможно (например, родительский узел расположен между и выше двух его дочерних узлов. И, как вы можете видеть, ни один из моего кода не управляет макетом вручную - graphviz, то есть dot , обрабатывает его автоматически.
import pygraphviz as PG
A = PG.AGraph(directed=True, strict=True)
A.add_edge("7th Edition", "32V")
A.add_edge("7th Edition", "Xenix")
# etc., etc.
# save the graph in dot format
A.write('ademo.dot')
# pygraphviz renders graphs in neato by default,
# so you need to specify dot as the layout engine
A.layout(prog='dot')
# opening the dot file in a text editor shows the graph's syntax:
digraph unix {
size="7,5";
node [color=goldenrod2, style=filled];
"7th Edition" -> "32V";
"7th Edition" -> "V7M";
"7th Edition" -> "Xenix";
"7th Edition" -> "UniPlus+";
"V7M" -> "Ultrix-11";
"8th Edition" -> "9th Edition";
"1 BSD" -> "2 BSD";
"2 BSD" -> "2.8 BSD";
"2.8 BSD" -> "Ultrix-11";
"2.8 BSD" -> "2.9 BSD";
"32V" -> "3 BSD";
"3 BSD" -> "4 BSD";
"4 BSD" -> "4.1 BSD";
"4.1 BSD" -> "4.2 BSD";
"4.1 BSD" -> "2.8 BSD";
"4.1 BSD" -> "8th Edition";
"4.2 BSD" -> "4.3 BSD";
"4.2 BSD" -> "Ultrix-32";
}