1. 程式人生 > >[完整步驟:安裝+配置+dot基本語法+程式碼實現動態畫圖] C++函式呼叫Graphviz軟體動態生成圖片

[完整步驟:安裝+配置+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;
}