目的
用Java或則Python畫出DAG圖、狀態轉換圖
應用場景:編譯原理等課程設計 畫自動機、狀態轉換圖、基本塊等有向圖
效果如下:
1.安裝
1.需要用到 graphviz,需要下載 http://www.graphviz.org/download/
但是上面下載太慢了,打包一個windows版的下載鏈接:https://www.yun.cn/s/31884bceaad04c3e8c630212babc0639
步驟A.下載:下載完解壓到某個文件夾就可以了。
步驟B.配置環境變量:添加剛才解壓的文件夾下的bin目錄到Path環境變量中
打開cmd,輸入dot -V 如果有顯示,就代表安裝成功、配置環境成功了。要是沒顯示就重新再來一遍。
2.畫圖
直接用安裝的graphviz里的dot工具畫一個圖試試
步驟A:新建一個txt文件
把下面的dot命令復制進去
digraph G {
A->B
B->C[label="a"]
C->D[label="1"]
}
打開cmd命令行,輸入以下命令,用dot工具就可以生成狀態轉換圖。
其中D:\graph\example.txt是剛才新建的txt文件的目錄文件名
其中D:\graph\example.jpg是想要生成圖片的文件目錄文件名稱
dot D:\graph\example.txt -T jpg -o D:\graph\example.jpg
這個時候,可以猜測到之前txt文本中的命令的含義,A->B就是結點A到B連一條有向邊,B->C[label="a"]就是結點B向結點C連一條有向邊,並且邊權寫上label=a
digraph G {
A->B
B->C[label="a"]
C->D[label="1"]
}
另外如果想給結點里面添加自定義的文字,也可以給結點添加lebl值,例如下圖和下面的指令所示
3.程序畫圖
可以用Java或則Python寫程序進行畫圖,Python更簡單網上有很多方法只需要導入庫,這篇文章主要來演示用Java來進行畫圖
暫時沒有Java相關的API。
所以要自己寫一個生成dot指令的,
dot指令就是下面的代碼,上文中我們寫好了代碼,在cmd下用dot命令就能生成圖了。
dot D:\graph\example.txt -T jpg -o D:\graph\example.jpg
digraph G {
A->B
B->C[label="a"]
C->D[label="1"]
}
如果要用Java程序實現,我們需要完成幾個功能:
1.生成dot指令(生成邊和結點,比如生成A->B這個字符串或則生成 A[label="write some"]這個帶有文字的結點)。
2.執行cmd下生成有向圖的命令。
3.自動打開生成的圖片。
所以就寫好了個工具類來玩。原理就是實現了上面幾個功能,復制文末附件代碼到本地,可以直接用。
example使用的例子:
public static void main(String[] args) throws IOException, InterruptedException {
GraphUtil graphUtil = new GraphUtil(); //new一個自己寫的GraphUtil類
graphUtil.setSourcePath("D:\\graph\\example.txt"); //設置dot指令的txt文件目錄
graphUtil.setTargetPath("D:\\graph\\example.jpg"); //設置要生成的圖片的目錄路徑
graphUtil.link("A","B"); //A結點向B結點 連一條有向邊 實際上就是字符串拼接,dot指令加上 A->B 這個字符串
graphUtil.link("B","C"); //B結點向C結點 連一條有向邊 實際上就是字符串拼接,dot指令加上 B->C 這個字符串
graphUtil.link("C","D"); //C結點向D結點 連一條有向邊
graphUtil.link("B","D","a"); //B結點向D結點 連一條有向邊
graphUtil.node("A","start"); //在A結點里面寫文字
GraphUtil.saveCodeToFile(graphUtil.getSourcePath(),graphUtil.getCode()); //保存dot指令到example.txt文件,graphUtil.getCode()獲取了dot指令的內容
GraphUtil.genAndOpenGraph(graphUtil.getSourcePath(),graphUtil.getTargetPath()); //生成圖片 並自動打開圖片文件
}
效果:
執行完上面Main函數的代碼,看到在指定的目錄下生成了txt指令文件,和jpg狀態轉換圖。
附:GraphUtil類(在本地新建一個命名 名稱為GraphUtil的class類,然后把下面代碼拷貝進去就可以了)
import java.awt.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class GraphUtil {
private String code = "digraph G {" + "\r\n";
private String sourcePath;
private String targetPath;
public void setTargetPath(String targetPath) {
this.targetPath = targetPath;
}
//節點A到節點B畫一條有向邊
public void link(String dotA, String dotB){
String linkCode = dotA + " -> " + dotB + "\r\n";
this.code += linkCode;
}
//節點A到節點B畫一條有向邊,邊權寫上label
public void link(String dotA,String dotB,String label){
String linkCode = dotA + " -> " + dotB + "[label=" + label + "]" + "\r\n";
this.code += linkCode;
}
public void node(String dot,String label){
String nodeCode = dot + "[label=\"" + label + "\"]" + "\r\n";
this.code += nodeCode;
}
//打開已經生成的DAG圖片
public static void openFile(String filePath) {
try {
File file = new File(filePath);
Desktop.getDesktop().open(file);
} catch (IOException | NullPointerException e) {
System.err.println(e);
}
}
//使用dot的命令 用dot指令文件 生成DAG圖片
public static void genGraph(String sourcePath,String targetPath) throws IOException, InterruptedException {
Runtime run = Runtime.getRuntime();
run.exec("dot "+sourcePath+" -T jpg -o "+targetPath);
Thread.sleep(1000);
}
//整合上面兩個方法的功能: 生成圖片后自動打開
public static void genAndOpenGraph(String sourcePath,String targetPath) throws InterruptedException, IOException {
genGraph(sourcePath,targetPath);
Thread.sleep(1000);
openFile(targetPath);
}
//保存dot指令到文件 后續利用這個指令文件 就可以用dot命令生成圖了
public static void saveCodeToFile(String filePath, String content) {
FileWriter fwriter = null;
try {
// true表示不覆蓋原來的內容,而是加到文件的后面。若要覆蓋原來的內容,直接省略這個參數就好
fwriter = new FileWriter(filePath, false);
fwriter.write(content);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
fwriter.flush();
fwriter.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
//一些setter和getter方法
public String getCode() {
return code + "\n}";
}
public void setCode(String code) {
this.code = code;
}
public String getSourcePath() {
return sourcePath;
}
public void setSourcePath(String sourcePath) {
this.sourcePath = sourcePath;
}
public String getTargetPath() {
return targetPath;
}
}