【问题标题】:Positioning of classes in UML diagramUML图中类的定位
【发布时间】:2012-04-04 23:45:04
【问题描述】:

我正在创建一个工具,用于将 Python 项目显示为 UML 图(+ 使用 GUI 显示一些代码错误检测)

我使用 Pyreverse 扫描了一些项目,我拥有绘制 UML 图所需的所有数据。 问题是类框在画布上的定位

首先,我决定使用已经实现的基于力的算法来决定类的位置,它工作得很好这是结果 https://github.com/jvorcak/gpylint/blob/master/screenshots/gpylint.png 这是code(Python,但即使对于非 Python 程序员也很容易理解)

有一个问题,它非常适合显示图形,但如果我想显示 UML,我希望有一些增强功能,例如,如果 2 个类扩展一个超类,我希望它们处于同一级别在图中就像graphs generated by dot program

你能告诉我一个算法如何做到这一点吗?或者至少给我一些想法?

【问题讨论】:

  • 您是否需要用户可以动态调整展示位置?如果没有,你可以使用graphviz的点程序为你做布局吗(graphviz.org)?
  • 谢谢您的回复,我已经提到我想知道点程序的算法(如果没有,我必须浏览它的源代码),因为我想绘制这些对象在画布上并允许用户与他们一起移动
  • 这可能很难,因为大多数 Python 程序都是用鸭子类型完成的。对于我的一些程序,我没有对所有类都使用继承。

标签: python uml drawing graph-drawing pyreverse


【解决方案1】:

您缺少的主要增强功能似乎是将您的图表转换为layered graph. 这是不容易的任务,但它是可行的。 (结果的质量可能会因过程中投入的时间和思想而异)。

主要思想是在图形上做某种topological sorting 以将其拆分为层,在其中进行一些排列,然后绘制图形。 (你可以在网上找到python代码来做一个真正的拓扑排序(example),但真正的TS只会产生一个长线状的图形,我们想要一些不同的东西)

所以我将尝试描述一种将给定图形转换为分层图形的算法:

  1. 拓扑排序不适用于带环的图,因此如果输入图还不是没有环的有向图,则必须找到一组可以删除(或可能反转)的边) 创建一个循环图(稍后您会将它们添加到分层图中,但这会破坏分层并使图不那么漂亮:)。由于找到可以删除的最小可能边集是 NP 完全的(非常困难) - 我认为你必须在这里做一些捷径,不一定要找到最小的边集,但要在合理的时间内完成。

  2. 将图分解成层,这里可以做很多优化,但我建议你保持简单。迭代所有图的顶点,每次收集所有没有传入边的顶点到一个层。在一些简单的情况下,这可能会生成类似线的图,但它非常适合 UML 图的情况。

  3. 好的图是边数最少的图crossing each other,这听起来并不重要,但这一事实对图的整体外观有很大贡献。决定交叉点数量的是每一层中边缘的排列顺序。但同样,找到交叉点的最小数量或找到最大的无交叉边集是 NP 完全的 :( “所以这又是典型的诉诸启发式方法,例如将每个顶点放置在一个位置,该位置通过找到其邻居在前一层的位置的平均值或中值来确定,然后交换相邻对,只要这样可以提高交叉的数量。”

  4. 算法第一步中移除(或反转)的边缘返回到其原始位置。

你有它!一个很好的 UML 分层图。

  • 如果我的解释不够清楚,请尝试再次阅读Layered graph drawing 上的维基百科文章,或者问我任何问题,我会尽力回复。
  • 请记住,这是针对一般情况的算法,可以进行大量优化以更好地处理您的具体情况。
  • 如果您想了解更多有关 UML 工具功能的想法,请查看 Jetbrains 为他们的 IntelliJ UML tool 所做的出色工作

希望我的 cmets 能对你有所帮助。

重要更新: 因为您说您“正在寻找来自可靠和/或官方来源的答案。” 我附上This 来自graphviz(dot's algorithm)的正式文档“描述了用于绘制有向图的四遍算法。第一遍使用网络单纯形算法找到最佳等级分配。第二遍通过迭代启发式设置等级内的顶点顺序结合新的权重函数和局部转置以减少交叉。第三遍通过构建和排序辅助图来找到节点的最佳坐标。第四遍使用样条线绘制边缘。该算法可以绘制良好的绘图并且运行速度很快。 http://www.graphviz.org/Documentation/TSE93.pdf

【讨论】:

  • +1 表示这个问题背后的底层图论。大多数强大的图形布局生成器将提供分层的数据的拓扑排序视图,特别是在保证图形是非循环的或具有修剪简单循环的规则的情况下(例如,UML、组织结构图和许多其他分层组织图空间中的数据)。这很好地解决了这个问题的算法部分,尽管我很想知道其中一种罐装解决方案是否能更好地满足他们的需求。 :)
  • 感谢您的评论。你当然是对的,自己实施一个令人满意的解决方案需要付出很多努力,唯一的好处是,结果将更容易控制和定制。如果那个单一的好处值得付出努力......那就由读者决定:)。我发布这个冗长答案的原因是@javo 说他“想知道点程序的算法”,这样他就可以对算法的机制有一些基本的了解。
  • 同意!这对我来说本质上也非常有用,因为它为空间提供了一个很好的设计概述。感谢您在这里发布。 :)
【解决方案2】:

连接组件的受限布局是一个重要的问题,您最好使用现有工具来解决。您提到了Graphviz,但我认为您不会找到一个直接的算法来移植到 Python。更好的解决方案可能是使用pydot 与 Graphviz 交互并让它处理布局。

流程看起来像:

  1. 为 UML 图生成数据
  2. 使用 pydot 转换为点语言
  3. 使用Graphviz工具布局,输出点语言包括布局
  4. 用 pydot 解析输出的布局
  5. 使用 Python 显示

Graphviz 处理布局,但所有显示仍在 Python 中,以允许您想要支持的任何自定义行为。

【讨论】:

    【解决方案3】:

    提供我自己的答案building on blahdiblah's,您确实可以使用建议的工作流程成功生成您的 UML 图。

    但是,这似乎是通向您的解决方案的花园路径,这对于您的应用程序设计来说似乎并不理想。具体来说,我们想通过reduce the number of theoretical moving parts required 来完成这项工作。

    我建议不要使用pyreverse,而是查看the alternatives mentioned in this thread。具体来说,Epydoc 之类的工具可能会更好地满足您的需求,既可以减少依赖关系,也可以减少其 (MIT) 许可结构。

    无论您选择哪条路径,祝您申请顺利。

    【讨论】:

      【解决方案4】:

      我不是 Python 程序员,但在功能上我可以给你一些建议。

      1. 你必须知道每节课的行数

      2. 保留班级的级别编号,以帮助您根据级别编号组织课程。

      【讨论】:

        【解决方案5】:

        如果您想以有序的方式显示班级(父母在上,孩子在下),您应该跟踪每个班级的“权重”。我所说的重量是指“父母”的数量。

        例如,如果 B 继承自 A,则 B.weight = 1 且 A.weight = 0。如果 C 继承自 B,则 C.weight = 2。如果将其表示为一条线,则将打印类 A在第 0 行,B 在第 1 行,C 在第 2 行。一般来说,所有具有相同“权重”的类都将打印在同一虚拟行上。

        当然这只是基本思想,如果你想支持复杂的对象(多继承等),定位元素会比这更困难。

        【讨论】:

          【解决方案6】:

          您不太可能从非 UML 优先开发的实际项目中获得好的结果。这是我们大约 10 年前使用第一个 java-uml 往返工具 (TogetherJ) 学到的一个教训。在文本模式下,很容易摆脱无法很好绘制的代码。作为一种深入了解代码的方法,smalltalk 系统的基于浏览器的动态视图比 UML 工具目前所能提供的要有效得多。

          对于布局,只需看看在 CAD 中为电子产品所做的所有工作,尤其是印刷电路板 (PCB)。那里有很好的放置和路由算法。我还没有看到自动化 UML 工具正确处理的一件事是处理大量子类,您希望布局从父类下方的单行类更改为低节点移动半个节点的双行。

          【讨论】:

            猜你喜欢
            • 2023-04-10
            • 2012-03-21
            • 1970-01-01
            • 2014-01-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多