8

PythonプロジェクトをUML図として表示するためのツールを作成しています(+ GUIを使用してコードエラー検出を表示します)

Pyreverseを使用していくつかのプロジェクトをスキャンし、UML図を描画するために必要なすべてのデータを持っています。問題は、キャンバス上のクラスボックスの配置です

まず、すでに実装されている力ベースのアルゴリズムを使用してクラスの位置を決定することにしました。これは非常にうまく機能します。結果は https://github.com/jvorcak/gpylint/blob/master/screenshots/gpylint.png です。コード(Pythonですが、Python以外のプログラマーでも理解しやすいです)

問題が1つあり、グラフを表示するのに最適ですが、UMLを表示したい場合は、いくつかの拡張機能が必要です。たとえば、2つのクラスが1つのスーパークラスを拡張する場合、グラフ内で同じレベルになると予想されます。ドットプログラムによって生成されたグラフのように

これを行う方法をアルゴリズムに教えてください。または、少なくとも私にいくつかのアイデアを教えてください。

4

6 に答える 6

8

不足している主な機能強化は、グラフを階層化されたグラフに変換することのようです。これは簡単な作業ではありませんが、実行可能です。(結果の品質は、プロセスに費やされた時間と考えによって異なる場合があります)。

主なアイデアは、グラフに対してある種のトポロジカルソートを実行してグラフをレイヤーに分割し、グラフ内でいくつかの配置を行ってから、グラフを描画することです。(実際のトポロジカルソートをオンラインで実行するPythonコードを見つけることができますが()、実際のTSは長い線のようなグラフを生成するだけなので、少し異なるものが必要です)

そこで、特定のグラフを階層化されたグラフに変換するアルゴリズムについて説明します。

  1. トポロジカルソートはサイクルのあるグラフでは機能しないため、入力グラフがまだサイクルのない有向グラフでない場合は、循環を作成するために削除(または反転)できるエッジのセットを見つける必要があります。グラフ(後でそれらを階層化されたグラフに追加しますが、それによって階層化が中断され、グラフの見栄えが悪くなります:)。削除できるエッジの可能な限り最小のセットを見つけることはNP完全(非常に難しい)であるため、ここでいくつかのショートカットを実行する必要があり、必ずしも最小のエッジのセットを見つける必要はないと思いますが、妥当な時間内にそれを実行します。

  2. グラフをレイヤーに分割します。ここで実行できる最適化は多数ありますが、単純にすることをお勧めします。グラフのすべての頂点を反復処理し、毎回、レイヤーへの入力エッジのないすべての頂点を収集します。これにより、単純な場合には線のようなグラフが生成される場合がありますが、UMLグラフの場合には非常に適しています。

  3. 良いグラフとは、交差するエッジの数が最も少ないグラフです。重要ではないように聞こえますが、この事実はグラフの全体的な外観に大きく影響します。交差の数を決定するのは、すべてのレイヤーのエッジの配置の順序です。ただし、交差の最小数を見つけるか、交差のない最大のエッジのセットを見つけることは、NP完全です:("したがって、これも一般的です。前のレベルでの隣接する位置の平均または中央値を見つけて、交差の数が改善される限り隣接するペアを交換することによって決定される位置に各頂点を配置するなど、ヒューリスティックに頼ります。」

  4. アルゴリズムの最初のステップで削除された(または反転された)エッジは、元の位置に戻されます。

そして、あなたはそれを持っています!UMLに適した階層化されたグラフ。

  • 説明が十分に明確でない場合は、レイヤードグラフ描画に関するウィキペディアの記事をもう一度読むか、質問をしてください。回答を試みます。
  • これは一般的なケースのアルゴリズムであり、特定のケースをより適切に処理するために多くの最適化を行うことができることを忘れないでください。
  • UMLツールの機能についてさらにアイデアが必要な場合は、JetbrainsがIntelliJUMLツールに対して行ったすばらしい作業をご覧ください。

ここでの私のコメントが何らかの形で役立つことを願っています。

重要な更新: 「信頼できるおよび/または公式のソースから描画する回答を探して いる」と述べたので、これを添付します。「 有向グラフを描画するための4パスアルゴリズムを説明する(ドットのアルゴリズムの)graphvizからの正式なドキュメント。最初のパスは、ネットワークシンプレックスアルゴリズムを使用して最適なランク割り当てを見つけます.2番目のパスは、交差を減らすために新しい重み関数とローカル転置を組み込んだ反復ヒューリスティックによってランク内の頂点の順序を設定します.3番目のパスは、補助グラフのランク付け。4番目のパスは、エッジを描画するためのスプラインを作成します。アルゴリズムは、優れた描画を作成し、高速に実行されます。」 http://www.graphviz.org/Documentation/TSE93.pdf

于 2012-04-14T14:54:25.923 に答える
3

接続されたコンポーネントの制約されたレイアウトは重要な問題であり、既存のツールを使用して解決する方がよい場合があります。Graphvizについておっしゃっていましたが、Pythonに移植するための簡単なアルゴリズムが見つかるとは思いません。より良い解決策は、pydotを使用してGraphvizとインターフェイスし、レイアウトを処理できるようにすることです。

フローは次のようになります。

  1. UML図のデータを生成する
  2. pydotを使用してドット言語に変換する
  3. Graphvizツールを使用したレイアウト、レイアウトを含むドット言語の出力
  4. 出力されたレイアウトをpydotで解析します
  5. Pythonを使用して表示する

Graphvizがレイアウトを処理しますが、サポートしたいカスタム動作を可能にするために、すべての表示はPython内にあります。

于 2012-04-10T18:47:50.177 に答える
2

blahdiblahに基づいて独自の回答を提供することで、提案されたワークフローを使用してUML図を正常に生成できます。

しかし、これはソリューションへの道をたどるようなものであり、アプリケーションの設計には望ましくないようです。具体的には、これを機能させるために必要な理論上の可動部品の数を減らしたいと考えています。

pyreverseを使用する代わりに、このスレッドで言及されている代替案を調べることをお勧めします。具体的には、 Epydocなどのツールは、依存関係の削減とその( MIT)ライセンス構造の両方でニーズをより適切に満たす可能性があります。

どのパスを選択するかに関係なく、アプリケーションで頑張ってください。

于 2012-04-10T22:00:55.193 に答える
0

クラスを順番に表示したい場合(親が上、子が下)、各クラスの「重み」を追跡する必要があります。私が体重で意味するのは「親」の数です。

たとえば、BがAを継承する場合、B.weight =1およびA.weight=0です。CがBを継承する場合、C.weight = 2です。これを行として表すと、クラスAは行0に出力されます。 、Bは1行目、Cは2行目です。一般的に、同じ「重み」のすべてのクラスは同じ仮想線に印刷されます。

もちろん、これは基本的な考え方です。複雑なオブジェクト(マルチヘリテージなど)をサポートする場合、要素の配置はこれよりも難しくなります。

于 2012-04-13T23:50:06.957 に答える
0

私はPythonプログラマーではありませんが、機能的には何かを提案することができます。

  1. 各クラスで使用する行数が必要です

  2. レベル番号に基づいてクラスを整理するのに役立つクラスのレベル番号を保持します。

于 2012-04-13T09:33:13.643 に答える
0

UMLファーストで開発されていない実際のプロジェクトから良い結果が得られる可能性はほとんどありません。これは、最初のjava-umlラウンドトリップツール(TogetherJ)を使用して約10年前に学んだ教訓です。テキストモードでは、うまく描画できないコードを簡単に回避できます。smalltalkシステムの動的なブラウザベースのビューは、UMLツールが現在提供できるよりも、コードの洞察を得る方法としてはるかに効果的です。

レイアウトについては、CAD for electronics、特にプリント回路基板(PCB)で行われるすべての作業を見てください。そこには優れた配置およびルーティングアルゴリズムがあります。自動化されたUMLツールが正しく機能するのを見たことがないことの1つは、多くのサブクラスを処理することです。この場合、レイアウトを親の下のクラスの1行から、下位ノードがノードの半分にシフトされた2行に変更します。

于 2012-04-15T07:38:23.303 に答える