Позиционирование классов на диаграмме UML
Я создаю инструмент для отображения проекта Python в виде UML-диаграммы (+отображение некоторого обнаружения ошибок кода с помощью GUI)
я сканирую некоторый проект с помощью Pyreverse, и у меня есть все данные, необходимые для рисования диаграммы UML. Проблема заключается в расположении ящиков класса на холсте
для начала, я решил использовать уже реализованный алгоритм на основе силы, чтобы решить о положении классов, он работает довольно хорошо вот результат https://github.com/jvorcak/gpylint/blob/master/screenshots/gpylint.png а вот и код (Python, но это легко понять даже для не программистов на Python)
есть одна проблема, она отлично подходит для отображения графиков, но если я хочу отобразить UML, я хотел бы иметь некоторые улучшения, например, если 2 класса расширяют один суперкласс, я ожидал бы, что они будут на том же уровне в графике, как в графики, созданные точкой программа
не могли бы вы посоветовать мне алгоритм, как это сделать? Или хотя бы дать мне некоторые идеи?
6 ответов
кажется, что основным улучшением, которого вам не хватает, является преобразование вашего графика в многослойный граф. Это не простая задача, но это выполнимо. (качество результата, может варьироваться в зависимости от времени и мысли, вложенные в процесс).
основная идея состоит в том, чтобы сделать какой-то топологическая сортировка на графике, чтобы разделить его на слои, сделайте в нем некоторые расположения, а затем нарисуйте график. (вы можете найти код Python, чтобы сделать настоящий топологическая сортировка онлайн (пример), но real TS будет просто производить длинный линейный график, и мы хотим что-то немного другое)
поэтому я попытаюсь описать алгоритм преобразования данного графа в слоистый:
топологическая сортировка не работает на графах с циклами, поэтому, если входной Граф еще не является направленным графом без циклов, вам нужно будет найти набор ребер, которые можно удалить (или, возможно, отменить), чтобы создать циклический граф (позже вы добавите их к слоистому графу, но это затормозит расслоение и сделает график менее красивым :). Поскольку поиск наименьшего возможного набора ребер, который вы можете удалить, является NP-полным (очень сложным) - я думаю, вам придется сделать здесь некоторые ярлыки и не обязательно найти минимальный набор ребер, но сделать это в разумное время.
тормоз графика в слои, есть много оптимизаций, которые можно сделать здесь, но я предлагаю вам сохранить его простой. повторите все вершины графика и каждый раз соберите все вершины без входящих ребер в слой. Это может привести к линейному графу в некоторых простых случаях, но это вполне подходит для графов UML.
хорошим графом является тот, который имеет наименьшее количество ребер скрещиваясь друг с другом, это не звучит важно, но этот факт вносит большой вклад в общий вид графика. что определяет количество пересечений порядок расположения ребер в каждом слое.Но опять же, нахождение минимального числа пересечений или нахождение максимального набора ребер без пересечений является NP-полным :( "поэтому снова типично прибегать к эвристике, например, помещая каждую вершину в положение, определяемое нахождением среднего или медианы положений ее соседей на предыдущем уровне, а затем заменяя соседние пары, пока это улучшает количество пересечений."
края удалены (или наоборот) на первом шаге алгоритма возвращаются в исходное положение.
и вот оно! хороший многослойный график для вашего UML.
- если мое объяснение не было достаточно ясным, попробуйте прочитать статью Википедии на слоистых графов снова или задайте мне какие-либо вопросы, и я постараюсь ответить.
- помните, что это алгоритм для общего случая, и можно сделать много оптимизаций для лучше займись своим конкретным делом.
- если вы хотите больше идей для функций для вашего инструмента UML, посмотрите на замечательную работу, проделанную Jetbrains для их IntelliJ UML tool
надеюсь, что мои комментарии здесь будут полезны в любом случае.
Важные Обновления: поскольку вы заявили, что вы "поиск ответа на основе достоверных и / или официальных источников." Я придаю этой Этот формальная документация от graphviz (алгоритма dot), которая " описывает четырехпроходный алгоритм рисования направленных графов. Первый проход находит оптимальное присвоение ранга с использованием алгоритма симплекса сети. Второй проход устанавливает порядок вершин внутри рангов итеративной эвристикой, включающей новую весовую функцию и локальные транспозиции для уменьшения пересечений. Третий проход находит оптимальные координаты для узлов путем построения и ранжирования вспомогательного графика. Четвертый проход делает сплайны нарисуйте края. Алгоритм делает хорошие рисунки и работает быстро." http://www.graphviz.org/Documentation/TSE93.pdf
ограниченная компоновка подключенных компонентов-нетривиальная проблема, для решения которой вам лучше использовать существующие инструменты. Вы упомянули Graphviz, но я не думаю, что вы найдете простой алгоритм порт на Python. Лучшим решением может быть использование pydot для взаимодействия с Graphviz и пусть он обрабатывает макет.
поток будет выглядеть так:
- генерировать данные для UML схемы
- преобразование в точечный язык с помощью pydot
- макет с помощью инструментов Graphviz, выводя язык точек, включая макет
- проанализируйте выведенный макет с помощью pydot
- отображение с помощью Python
Graphviz обрабатывает макет, но весь дисплей все еще находится в Python, чтобы разрешить любое пользовательское поведение, которое вы хотите поддержать.
предоставляя свой ответ дом на blahdiblah это, вы действительно можете использовать предлагаемый рабочий процесс для успешного создания диаграмм UML.
но это похоже на то, что он берет садовый путь к вашему решению, которое, похоже, не желательно для дизайна вашего приложения. В частности, мы хотим уменьшить количество теоретических движущихся частей, необходимых чтобы заставить это работать.
вместо использования pyreverse, Я рекомендую посмотреть в альтернативы, упомянутые в этой теме. В частности, такой инструмент, как Epydoc может удовлетворить ваши потребности лучше, как для уменьшения зависимостей, так и в его (MIT) структуры лицензирования.
независимо от того, какой путь вы выберете, удачи с вашим приложением.
Я не программист python, но функционально я могу вам что-то предложить.
вы должны иметь количество строк, которые вы будете иметь с каждым классом
сохранить номер уровня класса, который поможет вам организовать занятия на основе уровня нет.
Если вы хотите показать классы упорядоченным образом (родители сверху, дети снизу), вы должны отслеживать "вес" каждого класса. Под весом я подразумеваю количество "родителей".
например, если B наследует от A, B. weight = 1 и A. weight = 0. И если C наследуется от B, C. Вес = 2. Если вы представляете это как строку, класс A будет напечатан на строке 0, B на строке 1 и C на строке 2. Вообще говоря, все классы одного и того же" веса " будут напечатаны на одном и том же виртуальная линия.
конечно, это только базовая идея, элемент позиционирования будет более сложным, чем это, если вы хотите, чтобы tu поддерживал сложные объекты (мульти-наследственность и т. д.).
вы вряд ли получите хорошие результаты от реальных проектов, которые не были разработаны UML-first. Это урок, который мы узнали около 10 лет назад, используя первые инструменты Java-uml туда и обратно (TogetherJ). В текстовом режиме слишком легко уйти с кодом, который невозможно красиво нарисовать. Динамические браузерные представления системы smalltalk гораздо более эффективны как способ получить представление о коде, чем средства UML в настоящее время.
для макета, просто взгляните на вся работа выполнена в САПР для электроники, особенно печатных плат (ПХД). Там хорошие алгоритмы размещения и маршрутизации. Одна из вещей, которые я не видел, чтобы автоматический инструмент UML делал правильно, - это обработка многих подклассов, где вы хотите, чтобы макет изменился с одной строки классов ниже родительской на двойную линию с низкими узлами, сдвинутыми на половину узла.