Pythonだけで簡単に！Graphvizの図形をdraw.io形式で出力する！

#はじめに
pythonで作ったネットワーク図をdraw.ioで編集したい人向けの記事です。
①networkxでグラフのもとになるdotフォーマットのデータを作って、
②それをgraphviz2drawioを使ってDraw.ioで使える形式(xlm)で出力します

今のところ、とりあえず出来た、というだけですが、環境づくりに苦労したので、同じような沼にはまる人がいないといいなと思って書きます。

最後にはこういうものが出来上がります。
（VSCodeの拡張機能、Draw.io Integrationで表示しています）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/1732e072-542f-6433-ac0f-0a26e4af86cc.png" width="" height=400px > 

#ハマった点
Graphvizで描画するのに使うGraphvizのパッケージと、graphviz2drawioを使うためのGraphvizのパッケージが異なる。環境を別にしないとエラーになってしまう。
Graphviz単体でインストールして使うことを前提としたパッケージと、パッケージ内で完結するものがあるのかもしれない。
あるいはパッケージ間の相性？
今回は、Graphvizでの描画用の環境とは別に、graphviz2drawioで出力するための環境を用意しました。

#環境
OS Windows10 64bit
Python 3.7.7(Anaconda3-2019.03で作成)
※Graphvizはインストールしていません


#環境構築
Anacondaで、新しいPython3.7の環境を作ります（Datavizと名付けました）　2020年5月30日時点で、python3.7.7でした。

##標準パッケージのインストール
下記のパッケージをインストールします。標準パッケージにあります。
・networkx
・pydot (pydotplusではない）
・python-graphviz（これがないとimport graphvizしても動かないので忘れないように）

※慌てて標準のgraphvizを入れないように！

##標準外パッケージのインストール
ここからはAnacondaのターミナルを起動して、順番に
・graphviz （　https://anaconda.org/alubbock/graphviz ）ver 2.41
・pygraphviz　（ https://anaconda.org/alubbock/pygraphviz ）ver 1.5
・graphviz2drawio（ https://pypi.org/project/graphviz2drawio/0.2.0/ ）
を入れていきます。

```
(DataViz) C:\Users\(ユーザー名)>conda install graphviz pygraphviz -c alubbock
(DataViz) C:\Users\(ユーザー名)>pip install graphviz2drawio==0.2.0
```
とします。
標準のgraphvizを入れていると

```
pygraphviz/graphviz_wrap.c(2987): fatal error C1083: include ファイルを開けません。'graphviz/cgraph.h':No such file or directory
```

というエラーが出てpygraphvizがインストールできません。

## dot.exeのプラグイン登録（？）
これで、dotファイルを扱えるようになっているか確認するため`dot -v`と打ちます。
何かうまくいっていないと、次のように`dot -c`を入れるように言われます。


```
(DataViz) C:\Users\(ユーザー名)>dot -v
dot - graphviz version 2.41.20170921.1950 (20170921.1950)
There is no layout engine support for "dot"
Perhaps "dot -c" needs to be run (with installer's privileges) to register the plugins?
```


素直に`dot -c`（何も出ません）

```
(DataViz) C:\Users\(ユーザー名)>dot -c
(DataViz) C:\Users\(ユーザー名)>
```
もう一度`dot -c`を打って下記の結果が出ればあと少し。
これができていないと、graphviz2drawioを使ったときに "svg" is not recognized…　といったエラーが出ます。

```
dot - graphviz version 2.41.20170921.1950 (20170921.1950)

libdir = "C:\Users\(ユーザー名)\.conda\envs\DataViz\Scripts"
Activated plugin library: gvplugin_dot_layout.dll
Using layout: dot:dot_layout
Activated plugin library: gvplugin_core.dll
Using render: dot:core
Using device: dot:dot:core
The plugin configuration file:
        C:\Users\(ユーザー名)\.conda\envs\DataViz\Scripts\config6
                was successfully loaded.
    render      :  cairo dot dot_json fig gdiplus json json0 map mp pic ps svg tk vml xdot xdot_json
    layout      :  circo dot fdp neato nop nop1 nop2 osage patchwork sfdp twopi
    textlayout  :  textlayout
    device      :  bmp canon cmap cmapx cmapx_np dot dot_json emf emfplus eps fig gif gv imap imap_np ismap jpe jpeg jpg json json0 metafile mp pdf pic plain plain-ext png ps ps2 svg tif tiff tk vml xdot xdot1.2 xdot1.4 xdot_json
    loadimage   :  (lib) bmp eps gif jpe jpeg jpg png ps svg

```

##MxGraph.pyの修正
"C:\Users\(ユーザー名)\.conda\envs\DataViz\Lib\site-packages\graphviz2drawio\mx\MxGraph.py"
のファイルがエラーになるのですが、本来やろうとしていた処理がよくわからないのでコメントアウトして上書き保存しましょう。
分かる方は、ぜひ修正を手伝ってあげてください。　https://github.com/hbmartin/graphviz2drawio/issues

```
~\.conda\envs\DataViz\Lib\site-packages\graphviz2drawio\mx\MxGraph.py in add_mx_geo_with_points(element, curve)

    115        for cb in curve.cbset:
    116            ET.SubElement(array, MxConst.POINT, x=str(cb[0][0]), y=str(cb[0][1]))
--> 117        if cb:
    118            ET.SubElement(array, MxConst.POINT, x=str(cb[1][0]), y=str(cb[1][1]))
    119
UnboundLocalError: local variable 'cb' referenced before assignment
```


修正後

```python:MxGraph.py
        for cb in curve.cbset:
            ET.SubElement(array, MxConst.POINT, x=str(cb[0][0]), y=str(cb[0][1]))
        #エラーになるのでコメントアウト
        #if cb:
        #    ET.SubElement(array, MxConst.POINT, x=str(cb[1][0]), y=str(cb[1][1]))

```

##環境構築の補足

2020年5月30時点で、python3に対応しているpygraphvizは、alubbockのものしかなく、それに合わせてalubbockのgraphvizを入れておく必要があるようです。ただし、このgraphvizを入れても、グラフは描画できませんでした。




```python:graphviz_test.py
from graphviz import Source
path = "edges.dot"
s = Source.from_file(path)
s.view()
```
結果

```
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
//
FileNotFoundError: [WinError 2] 指定されたファイルが見つかりません。
During handling of the above exception, another exception occurred:
ExecutableNotFound                        Traceback (most recent call last)
//
ExecutableNotFound: failed to execute ['dot.bat', '-Tpdf', '-O', 'pdt.dot'], make sure the Graphviz executables are on your systems' PATH
```


逆に、標準のgraphvizを入れると、graphviz2drawioが使えず（内部で呼び出しているpygraphvizが使えない）

`from graphviz2drawio import graphviz2drawio`
を実行すると

```
ImportError: DLL load failed: 指定されたモジュールが見つかりません。
```
となってしまいます。

Windowsじゃなかったり、環境構築をpipでやったり、Graphvizのソフトをインストールして適切なPATHの設定をすればこんな苦労はないのかもしれません。


#使ってみる
##プログラム

今回はこのサンプルを使います。edgelist2drawio.py と同じフォルダに保存している前提です。

```text:edges.txt
A B
A C
A D
B C
C D
D E
```

pythonプログラム

```python:edgelist2drawio.py
import networkx as nx
from graphviz2drawio import graphviz2drawio

#edges.textを有向グラフとして読み込む
G = nx.read_edgelist('edges.txt', create_using=nx.DiGraph(), nodetype=str)

# 読み込んだ情報をdot形式に変換
edges_dot = nx.drawing.nx_pydot.to_pydot(G)
# 必要ならdotファイルとして書き出す
# nx.drawing.nx_pydot.write_dot(G,"edges.dot")


# dot形式のデータをもとにgraphviz的な描画をしてdraw.ioファイルとして書き出す
xml = graphviz2drawio.convert(str(edges_dot))

# dotファイルから読み取って変換するときは
# xml = graphviz2drawio.convert("edges.dot")

# 変換したファイルを保存
with open('edges.drawio', 'w') as f:
  print(xml, file=f)
```
##編集する
プログラムを実行して作られるedges.drawioをVS Code（拡張機能でDraw.io Integration https://github.com/hediet/vscode-drawio.git を入れておく）で開くと、Graphvizで書いたような図が開けます。
あとは思いのままに編集できます！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/1732e072-542f-6433-ac0f-0a26e4af86cc.png)



#おわりに
・graphviz2drawio
　https://pypi.org/project/graphviz2drawio/0.2.0/

・GraphViz
　https://www.graphviz.org/

・VS CodeでDraw.ioが使える喜びはこちらの記事で知りました。
　「VSCodeでDraw.ioが使えるようになったらしい！」
　https://qiita.com/riku-shiru/items/5ab7c5aecdfea323ec4e


・