[完整步驟:安裝+配置+dot基本語法+程式碼實現動態畫圖] C++函式呼叫Graphviz軟體動態生成圖片
引言:
最近實現課程專案實踐考核,要求將自動機生成的中間結果以圖的形式展現出來。就隨手學了一下Graphviz這個軟體,非常的簡單好用,下面將使用C++函式利用Graphviz生成對應圖片的方法進行如下總結。
安裝和配資:
安裝和配置十分簡單,下面以windows為例:
(1)下載:
在官網直接進行下載:https://graphviz.gitlab.io/download/
下載2.38穩定版的.msi檔案
(2)安裝配置
下載後直接雙擊可執行檔案進行安裝即可,記住安裝路徑,方便之後的環境變數的配置。
例如我的安裝路徑為:D:\graphviz
我們將 D:\graphviz\bin 新增到 環境變數Path中:
配置完成後使用可能需要重啟電腦,使環境變數修改生效
dot基本語法:
(1)開啟Gvedit.exe:安裝完成後不會生成快捷方式,要在相關路徑中查詢,查詢到後可以生成方式,儲存在桌面。
(2)基本語法:
在.gv檔案中輸入下面程式碼,按F5生成圖片。
digraph G{
a -> b[label="{f}"];
a -> c[label="{g}"];
b -> c[label="{s}"];
}
我們看看程式碼就可以知道
在大括號{ }中每一個;號代表一條邊
-> 兩遍代表有向邊的兩個節點名
[label=“xxxxx”]代表邊上的轉移關係
更多更詳細的有關畫圖屬性的語法說明,可以上網搜尋
(3)cmd命令列執行:
** Step 1: ** 首先,需要編輯dot指令碼
可以使用你熟悉的純文字編輯器進行指令碼編寫(必須是純文字編輯器,如vim、notepad++,像word這樣的富文字編輯器是不行的),只需設定編碼為UTF-8。
編輯下面的指令碼程式碼,儲存為test.dot(先不用管其具體的意思,直接複製就行了):
digraph G{ main -> parse -> execute; main -> init; main -> cleanup; execute -> make_string; execute -> printf; init -> make_string; main -> printf; execute -> compare; }
Step 2: 隨後,選用佈局生成結果
使用如下命令生成結果:
dot -Tpng sample.dot -o sample.png
對於這條命令,dot表示用dot佈局,-Tpng表示生成png圖片格式,sample.dot是指令碼檔名,-o sample.png表示生成輸出的圖片名稱。
改命令也可以寫成dot -Kdot -Tpng sample.dot -o sample.png,其中-Kdot表示使用dot佈局。
C++函式實現圖片生成: 環境codeblocks
(1)最簡單的呼叫:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
void gen_graphic()
{
freopen("graph.dot","w",stdout);
cout<<"digraph G{"<<endl;
cout<<"main -> parse -> execute;"<<endl;
cout<<"main -> init;"<<endl;
cout<<"}"<<endl;
fclose(stdout);
system("dot -Tpng graph.dot -o sample.png");
}
int main()
{
gen_graphic();
return 0;
}
將相關dot語句直接使用cout寫入graph.dot檔案中
呼叫system函式執行系統命令 dot -Tpng graph.dot -o sample.png 生成圖片。
從上面的例子我們可以看出,使用Graohviz生成圖片的關鍵在於,我們只需要生成所需拼接處表示邊的字串即可。
(2)動態生成有向圖:
在codeblocks中新增grapg_in.txt檔案,其中包含建圖需要的四元組資訊。(以從某一txt檔案中讀入動態資訊為例,也可以直接從某一陣列或資料結構中讀入資料)。包含以下資訊:
({f},s,{f,g},{null})
({f},q,{f},{g})
({f},p,{g},{f})
({g},s,{f,g},{null})
({g},q,{f},{g})
({null},true,{f,g},{null})
一個四元組表示一條有向邊,第一個元素為邊尾節點,最後一個元素為邊頭結點,中間兩個元素為邊上轉移關係。
使用以下函式生成對應的graph.dot檔案:
執行 readGraph(“graph_in.txt”,“graph.dot”);
//引數說明:
//string input:包含四元組的txt檔名
//string output:對應生成的dot檔名
void readGraph(string input,string output) //.txt 轉 .dot
{
const char* in = input.data();
const char* ou = output.data();
freopen(in,"r",stdin);
freopen(ou,"w",stdout);
cout<<"digraph G{"<<endl;
string s;
while(cin>>s)
{
string u,v,lable;
int n = s.length();
int i = 1;
u = "\"";
while(s[i] != ',' || s[i-1] != '}') i++;
u += s.substr(1,i-1);
u += "\"";
v = "\"";
int j = n-2;
while(s[j] != ',' || s[j+1] != '{') j--;
v += s.substr(j+1,n-1-j-1);
v += "\"";
lable = s.substr(i+1,j-i-1);
string edge = "";
edge += u;
edge += "->";
edge += v;
edge += "[label=\"";
edge += lable;
edge += "\"];";
cout<<edge<<endl;
}
cout<<"}"<<endl;
fclose(stdin);
fclose(stdout);
}
結果:
根據生成的dot檔案生成圖片:
執行: makeGraph(“graph.dot”,“graph4.png”);
//引數說明:
//string inputname:dot檔名
//string outputname:生成的png檔名
void makeGraph(string inputname,string outputname) //生成png圖片
{
string s = "";
s += "dot -Tpng ";
s += inputname;
s += " -o ";
s += outputname;
const char* cmd = s.data();
const char* iname = inputname.data();
system(cmd);
}
結果:
完整程式碼:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
void readGraph(string input,string output) //.txt 轉 .dot
{
const char* in = input.data();
const char* ou = output.data();
freopen(in,"r",stdin);
freopen(ou,"w",stdout);
cout<<"digraph G{"<<endl;
string s;
while(cin>>s)
{
string u,v,lable;
int n = s.length();
int i = 1;
u = "\"";
while(s[i] != ',' || s[i-1] != '}') i++;
u += s.substr(1,i-1);
u += "\"";
v = "\"";
int j = n-2;
while(s[j] != ',' || s[j+1] != '{') j--;
v += s.substr(j+1,n-1-j-1);
v += "\"";
lable = s.substr(i+1,j-i-1);
string edge = "";
edge += u;
edge += "->";
edge += v;
edge += "[label=\"";
edge += lable;
edge += "\"];";
cout<<edge<<endl;
}
cout<<"}"<<endl;
fclose(stdin);
fclose(stdout);
}
void makeGraph(string inputname,string outputname) //生成png圖片
{
string s = "";
s += "dot -Tpng ";
s += inputname;
s += " -o ";
s += outputname;
const char* cmd = s.data();
const char* iname = inputname.data();
system(cmd);
}
void gen_graphic()
{
freopen("graph.dot","w",stdout);
cout<<"digraph G{"<<endl;
cout<<"main -> parse -> execute;"<<endl;
cout<<"main -> init;"<<endl;
cout<<"}"<<endl;
fclose(stdout);
system("dot -Tpng graph.dot -o sample.png");
}
int main()
{
// gen_graphic();
readGraph("graph_in.txt","graph.dot");
makeGraph("graph.dot","graph4.png");
return 0;
}