Graphviz как пользоваться windows

Обновлено: 07.07.2024

Graphviz — утилита командной строки для генерации схем графов на основе текстовых описаний узлов и рёбер. Из статьи вы узнаете, как и языка C++ программно вызвать graphiz с заданными параметрами, прочитать вывод и код возврата вызванной утилиты. Способ можно использовать в отрыве от Graphviz, здесь он взят для примера.

Установка graphviz

Чтобы получить graphviz на Windows, вам нужно:

Для Ubuntu потребуется:

После установки проверьте работу graphviz на простом примере:

Автоматизируем вызов Graphviz

Для автоматизации нам потребуется кроссплатформенный способ запуска внешнего процесса из C++. Например, функции popen / pclose из стандарта POSIX.

макрозамена popen/pclose
объявление класса CUtils
запуск внешней команды в CUtils::RunProcess

Класс CPipeHandle — это простейшая безопасная обёртка над FILE* , в деструкторе которой автоматически вызывается закрытие канала через pclose .

RAII-обёртка для FILE*

Чтобы воспользоваться API, нужно указать полную команду со всеми аргументами. Если путь к исполняемому файлу команды не добавлен в переменную окружения PATH, то придётся указать полный путь к .exe . Вот пример вызова Graphviz для преобразования диаграммы fsm.dot в изображение fsm.jpg

Класс генерации кода для Graphviz

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

Иллюстрация

На выходе мы должны получить примерно такой файл *.dot :

В файле указаны надписи на вершинах и рёбрах графа, где под вершинами подразумеваются состояния, а под рёбрами — переходы. Для вершин указана форма: для начального состояния круг (circle), для конечных состояний (терминалов) двойной круг (doublecircle), для промежуточных состояний (нетерминалов) прямоугольник (box).

Вспомогательный класс для сериализации назовём CDotWriter. Определение класса:

Мы создаем ПО для разработки и поддержки баз данных Oracle, и статический анализатор PL/SQL является одной из основных фич наших приложений. Кто знаком с Oracle, тот хорошо знает что такое PL/SQL.

Известная поговорка гласит: «Лучше один раз увидеть, чем сто раз услышать». Поэтому мы решили заимпрувить статический анализатор таким образом, чтобы он визуализировал код в виде блок-схем (Flowcharts) и диаграмм вызовов (Call Trees). Хоть и нарисовать блоки и их связи несложно, оптимизировать их расположение на «листе» представлялось задачей, требующей значительных усилий. Чтобы стрелки минимально пересекались и обтекали блоки, блоки объединялись в группы, и диаграмма при этом не превращалась в «кашу», нужно было потратить много сил и времени.

И тогда мы решили поискать готовое решение, дабы не изобретать велосипед. Наше внимание сразу привлек Graphviz – open source решение по визуализации диаграмм. Первые его версии были разработаны компанией AT&T, а теперь он доступен как набор утилит и библиотек, а также в исходниках под лицензией Eclipse Public License (EPL).

Его движок диаграмм использует язык описания графов DOT, который представляет собой текстовое описание структуры графа: вершины, их связи, группы и атрибуты для их визуального оформления.

Описание простейшего графа:


Если необходимо добавить узлам атрибуты, например подписи, то необходимо отдельно описать узлы:


Далее покажу на примере простой процедуры на PL/SQL:


Код понятен, даже если вы не знакомы с синтаксисом PL/SQL.

А теперь опишем этот код на языке DOT. Пояснения снова излишни:


Теперь можно «скормить» этот файл Graphviz, подставив соответственно вместо %PNGFILE% и %DOTFILE% имена выходного (png) и входного (dot) файлов:


При помощи Graphviz из описания выше получается такая картина:


Наглядно, но весьма аскетично.

Можно сделать диаграмму привлекательнее, добавив атрибуты для определения формы блоков (shape=diamond) , подписи стрелок (label="Yes") и цвета color , fontcolor .

Также мы можем объединить в подграф блоки, которые являются исполняемыми. Это реализуется конструкцией subgraph <> , внутри которой мы можем перечислить имена блоков, включенных в подграф, и указать атрибуты для визуального оформления (цвет рамки).


Получается вполне симпатичная блок-схема, которую и людям показать не стыдно.


Чтобы код могли читать не только программисты, но и «простые смертные», мы придумали такую фишку: приделали распознавание специальных тегов в комментариях, которые бы разработчик писал как псевдокод – язык описания алгоритмов. Это помогло увеличить ценность блок-схемы, которая теперь может отображать бизнес-логику, а не только визуализировать листинг.


Вуаля! Graphviz дает возможность делать потрясающие визуализации с минимальными затратами.

Однажды, собираясь писать документацию - я задумался. Проект, который я собирался документировать, динамически развивался и было очевидно, что все схемы, которые я буду рисовать ещё не раз придётся дополнять и перерисовывать. "Вот бы было что-то типа Wiki, но с возможностью также легко рисовать схемы" - подумал тогда я. "Но неужели до меня никто не додумался до столь простой мысли и не сделал столь полезного изобретения?" - меня посетили сомнения. "Не может быть!" Я отправился в поисковые системы, где во множестве предлагались различные платные графические редакторы, позволяющие рисовать прямо в браузере. Но тут мой взгляд зацепился за знакомое название GraphViz! Вспомнив, что я уже не раз слышал об этом продукте, а также не раз видел его в составе моего дистрибутива, я решил познакомится поближе и неожиданно увлёкся.

Автор статьи: Виктор Вислобоков
Размещается под лицензией: CC-BY-NC-ND

Установка

Установка до безобразия проста. GraphViz входит в состав практически всех извесных мне дистрибутивов, ориентированных на пользователя. Так что всё что остаётся, например, людям использующим Fedora, CentOS и прочие rpm-дистрибутивы с yum, это выполнить команду:

Также в дистрибутивах есть пакеты, позволяющие использовать GraphViz со многими языками прогаммирования: Perl, Python, PHP, Tcl/TK и и.д. Возможно после ознакомления с GraphViz вы захотите поставить и использовать также и эти пакеты.

Первое использование

Использовать GraphViz очень просто. Как всегда всё в стиле Unix-way, т.е. через командную строку. Вы делаете файл, в котором, на некоем мета-языке содержится описание требуемого вам графа, а затем запускаете утилиту из комплекта GraphViz, которая из этого файла делает другой файл, хранящий в себе уже сам отрисованный граф.


Простейший пример. Сделаем файл example1.gv вида: и получим из него картинку в формате PNG:

Думаю, теперь вы уже можете без труда составить простейшие вертикальные графы.

Разумеется, отрисованный граф можно получать не только в виде PNG-картинки. Это может быть и SVG-графика, встраиваемая в веб-страницу, и PDF, и PostScript, VML и т.д. и т.д.

Немного теории

Хотя с помощью GraphViz можно рисовать схемы, которые лишь отдалённо напоминают графы, всё же GraphViz - это именно средство для отрисовки графов! Поэтому не ждите от неё возможностей Visio или чего-то подобного, у этого продукта (как впрочем и любого другого) есть свои ограничения и недостатки, о чём будет сказано ниже. И вот вам кстати ссылка на Галерею графов, где вы можете посмотреть, что можно получить с помощью GraphViz.

Прежде чем пойти дальше, я введу некоторые термины, которыми мы будем оперировать. Они пригодятся ниже, когда я буду давать пояснения, а также если вы вдруг всё же начнёте изучать полную документацию. Итак:

  • node - Узел графа. Обычно представляет из себя какую-либо фигуру из библиотеки встроенных фигур.
  • edge - Переводится как "край", но в GraphViz имеет совсем другое значение. Обычно это стрелка, соединяющая node (узлы графа)
  • subgraph - Подграф, который включается в основной граф. Самостоятельная область графа, которая может включать в себя другие узлы и которая затем самостоятельно отрисовывается в графе
  • cluster - Или "кластер". Это разновидность подграфа, которая может иметь метку и выделенную область, выделяемую цветом.
  • атрибут - Именованный параметр, который может изменять оформление или поведение графа, подграфа, node или edge. Атрибутов великое множество и подробное описание к ним можно найти на официальном сайте, ссылку на который я уже приводил.

Имена узлов

GraphViz по умолчанию заточен на использование UTF-8 и вы можете преспокойно использовать русские буквы в названии узлов графа. Единственное, что необходимо помнить, что лучше заключайте любые имена в двойные кавычки, хотя GraphViz и позволяет использовать имена из одного слова, не содержащие разделительных знаков без кавычек. Т.е. вы можете использовать следующие имена узлов: Обратите внимание, в двух последних случаях использование двойных кавычек обязательно, потому что в имени "Новый год" два слова, разделённые через пробел, а имя "self-made" содержит служебный символ "-". В общем мой совет - используйте кавычки всегда и не ошибётесь, а напротив избежите досадных ошибок, связанных с невнимательностью.

В любом месте файла вы можете использовать комментарии также как в языке C++, т.е. либо вот так: либо вот так:

Умолчания

Если вы используете набор однотипных фигур и стрелок, но не тот, который установлен по умолчанию, гораздо удобней будет выставить вначале необходимые умолчания, которые будут действовать для всего графа. Например умолчание: приведёт к тому, что вместо эллипсов, внешний вид узлов примет вид прямоугольников, а все стрелки будут иметь два кончика, указывающие в обоих направлениях.

Примеры

Чтобы не описывать каждый раз подробно один из атрибутов, я просто приведу здесь несколько примеров, комбинируя которые вы и сможете в большинстве случаев получить то, что вам нужно. Примеры, я привожу исходя из того, что когда-то искал сам, так что надеюсь, что вы тоже оцените их полезность


Пример 1. Предположим вы хотите составить себе план действий на неделю, потому что периодически, что-либо вылетает из головы. Например, во вторник и субботу вы моете свой автомобиль, а по понедельникам, четвергам и субботам у вас тренировка в спортзале. А в среду, пятницу и понедельник вам нужно зайти в магазин за продуктами. А в четверг вам ещё нужно побывать в паспортном столе, чтобы забрать документы, а если они ещё будут не готовы, то подождать их сидя в кафе неподалёку. Теперь, сразу решаем, что внешний вид узлов графа должен быть круги вместо эллипсов, которые залиты синим цветом, а надписи в них будут белые. Поскольку документы - это наиболее важное в нашем плане, то мы используем красный цвет. А стрелки мы хотим, чтобы были не сплошные, а пунктирные. Да и граф нужно ориентировать не по вертикали, а по горизонтали, потому что так нам наглядней. В итоге получаем: И картинку:

Посмотрев на граф, вы видите, что он получился не таким уж и удобным. Дни недели разбросаны по всему графу, ориентироваться в нём неудобно. Может быть попробовать всё же изобразить его вертикально?


Пример 2. Давайте попробуем изобразить ранее изученный пример вертикально. Ну а чтобы было интересней, заключим дни недели в кластер, выделим его цветом и добавим метку. А вместо кругов в качестве фигур для дней недели будем использовать прямоугольники, залитые зелёным цветом. А шрифт у названий дней недели сделаем чёрного цвета и помельче. Заодно ещё и добавим в воскресенье сперва поход в театр, а затем в кафе: и картика:

Думаю, вы уже поняли, почему дни недели вверху выстроены не по порядку. Если нет, то присмотритесь внимательней - GraphViz распределил на графе узлы так, чтобы обеспечить наименьшее пересечение стрелок друг с другом. Пересечение стрелок может запутать, поэтому GraphViz оптимизирует свою работу при построении графа именно так.

Ещё обратите внимание, чтобы была возможность сделать рамочку и подпись к кластеру, имя у подграфа должно начинаться с "cluster" и никак иначе


Пример 3. Теперь давайте возьмём так сказать один день из жизни и сделаем из него блок-схему алгоритма. Пусть это будет четверг. Делаем практически то, что уже пробовали, лишь добавляем к стрелочкам, соединяющим узлы подписи "Да" и "Нет": получаем картинку:

И. разочаровано на неё смотрим! Коряво получилось. "Кафе" должно быть сбоку от условия "Если готовы документы", а у нас оно внизу, а стрелка к "Кафе" и обратно к "Если готовы документы" идёт совсем не по стандарту. Если изменить уровнь у "Кафе" ещё можно, использовав распределение узлов по уровням, то со стрелками уже ничего поделать нельзя. Вот вам и первый недостаток GraphViz - он не ориентирован на создание прямолинейных блок-схем алгоритмов.


Пример 4. Давайте теперь ознакомимся с возможностью GraphViz распределять узлы графа по определённым уровням высоты. Реализована эта возможность, через создание в качестве некой "линейки уровней" ещё одного невидимого графа в стороне от основного и закрепление узлов нашего графа на том же уровне, что узлов линейки. Итак, снова берём наш план и меняем его вот так: В итоге получим вот такую смешную картинку:

Обратите внимание, у узлов линейнки, установлен атрибут: который запрещает отрисовывать эти узлы, хотя и OpenViz резервирует место под них, а атрибут у стрелок, приводит к тому, что они отрисовываются, но белым цветом, таким же как цвет фона, поэтому их не видно!

Как вы можете видеть, теперь дни недели упорядочены по высоте, но к сожалению не находятся друг под другом. И увы, мне неизвесно способа как их можно заставить выстроится именно так! Конструкция: говорит, что узлы "2" и "Вторник" должны находится на одном уровне. Вы можете, например, добавить ещё какой-либо узел после "Вторник" также через ";" и этот узел также будет отрисован на этом же уровне.

Ещё один недостаток такого распределения по уровням становится ясен, когда начинаете работать с кластерами. Я пытался создать в кластере ещё один кластер и несколько узлов, распределив их по высоте, но сколь я не бился - у меня ничего не вышло. По неизвестной мне причине, уровни начинали вставать попарно друг с другом рядом. Если кто знает почему так и как это решить - напишите мне и моё "большое спасибо" вам обеспечено. Заодно дополню статью.


Пример 5. А что если хочется странного, например, чтобы название узла было в две строки? Это как раз довольно просто. А если вдруг хочется чтобы вторая строка имела другой цвет и размер шрифта? Вот это уже гораздо сложнее! А бывает вообще хочется некий прямоугольник, разбитый по секциям с разными надписями. Вот вам пример, реализующий всё вышесказанное: И картинка:

Как видите, если "box1" и "box2" используют тип узла "box" и HTML-форматирование, то "box3", "box4" и "box5" используют специальный тип узла: "record". К сожалению у "record" есть недостаток - расположение меток зависит от направленности графа, т.е. если бы у этого графа атрибут "rankdir" был выставлен в "LR" а не как по умолчанию в "TB", то метки "слева" и "справа" стали бы отрисоваться не слева и справа, а сверху и снизу и наоборот метки "снизу" и "сверху" стали бы отрисовываться вместо снизу и свеху, справа и слева. Это не очень-то удобно, если вы по каким-то причинам решить поменять направление роста графа.


Пример 6. Тем не менее, GraphViz неплохо подходит для отображения структуры баз данных и связей между таблицами. Приведём пример, заодно пользуясь случаем показать, что можно изменить внешний вид наконечников стрелок: и картинка:

Заключение

В общем-то пожалуй и всё, что я хотел рассказать. Думаю, для решения множества задач - этих примеров достаточно.

Хочется заметить, что во всех примерах я использовал направленный граф и утилиту dot, однако GraphViz позволяет использовать и другие виды отрисовки графов, такие как "circo", "neato" и т.д. Так вот, чтобы попробовать отобразить свой граф в таком режиме, нужно вместо dot использовать соответствующую утилиту: circo, neato и т.д. Не все атрибуты, которые поддерживаются dot, поддерживаются другими способами отображения - нужно иметь это в виду.


Достаточно часто встречается задача, когда надо нарисовать нечто, представляющее из себя граф. Это может быть иерархическая сструктура работ проекта, иерархическая структура рисков, организационная структура, топология сети и т.п. Если же количество вершин и ребер достаточно велико, то нарисовать это красиво становится нетривиальной задачей. К счастью, существует программа Graphviz, которая использует язык описания графов dot и имеет графический интерфейс под Windows.

Рассмотрим направленный граф, описывающий все возможные коммуникации между четырьмя участниками проекта. Листинг, описывающий граф привожу ниже:

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

bezymyannyy_1

В данном случае, был использован параметр по умолчанию и для построения графа использована утилита dot.

Для примера, тот же граф, построенный с помощью:

test_1
test_3
circo fdp twopi

Результат выглядит так:

test_4

Пример из заметки по динамическому программированию: меняем ориентацию графа (строится справа налево), добавляем подписи и стили линий и задаем размер листа.

step4-1

Более сложные пример приведен в заметке по методу анализа иерархии.

bezymyannyy_2

И последний пример, использование структуры в качестве вершины графа.

test_5

snimok-ekrana-2016-03-09-v-21-55-07

Следует отметить, что в Mac OS X при установке graphviz такой удобной графической оболочки как в Windows, вы не получите. Если вариант собрать его из исходников из портов (если вы хоть что-то поняли из написанных слов, значит инструкции не нужны), можно использовать консоль, как это показано выше, но также имеется возможноть использовать средства языка R и RStudio. Возможно, что кому-то это будет проще.

Для корректной работы вам понадобится пакет DiagrammeR. Установите его из меню или командой

bezymyannyy_3

Дальше, используя кнопку Export, можно сохранить получившуюся диаграмму в формате png или jpeg.

Следует отметить, что DiagrammeR позволяет работать и с диаграммами в формате mermaid. В таком случае, файлы надо сохранять с расширением .mmd. Помимо простых графов, этот формат позволяет описать диаграмму последовательности, как это показано на рисунке ниже.

bezymyannyy_4

rplot_1

К счастью, в формате mermaid можно редактировать online с сохранением результата в формате SVG, где результат будет выглядеть больше похожим на то, что ожидалось.

Читайте также: