利用C++&Graphviz完成繪圖任務
阿新 • • 發佈:2021-01-07
問題背景
Graphviz是個好東西啊,但是可惜的是我並沒有找到可以直接用C++畫圖的程式?(可能是我找的不對勁?
需求: 完成一個C++類,能夠通過C++類中的函式呼叫等方法完成繪圖工作。
前提: 已經完成Graphviz的安裝,並且把對應的路徑加入到系統的PATH中,即“前傳”部分已經完成。
解決方案
- 完成一個C++畫圖類,類比python networkx那種,可以呼叫函式來畫圖。
- 建立一個臨時檔案,按照過程寫入Graphviz的dot檔案
- 能夠區分有向圖跟無向圖
- 未完待續…
應用例項
//main.cpp
#include "DrawGraph.h"
int main() {
DrawGraph d;
d.Start();
d.Create_Node("TEST1");
d.Create_Node("TEST2");
d.Create_Relationship("TEST1", "TEST2", DrawGraph::Graph, "knows");
d.End();
d.Get_Png("H:/test.png");
return 0;
}
生成圖片:
Future Work
- 增加節點的多種屬性設定,基本按照Graphviz的Attribute來設定
- 增加子圖模式,允許在整張圖之中增加子圖。
- 增加邊的多種屬性模式,包括形狀顏色等。
- 提供面向物件的建模方式,方便快速排版生成的dot檔案。
- 允許對其中的節點進行層級排布,允許加入順序關係。
- 支援html等格式的初始化節點。
- 支援從鄰接矩陣、鄰接表中匯入圖形。
- 未完待續…
Code
//DrawGraph.h
#pragma once
#include <map>
#include <string>
#include <fstream>
class DrawGraph
{
private :
std::map<std::string, int> str2int;
std::map<int, std::string> int2str;
std::string temp_file = "./temp.tp";
int node_cnt = 0, rela_cnt = 0;
public:
static const int DiGraph = 1, Graph = 0;
void Start(int mode = 0);//開始畫圖的之前需要選定畫圖模式,DrawGraph::DiGraph or DrawGraph::Graph
int Create_Node(std::string node_name);//建立一個節點,並且節點名稱為node_name
int Create_Node(int node_name);//建立一個節點,節點名為一個整數數字
int Find_Node(std::string node_name);//根據節點名稱找到節點編號(方便上層開發的時候,不需要存下每個建立過的節點的具體名字,只需要存數字即可)
std::string Find_Node(int node_id);//通過節點的編號找到節點的名稱
int Create_Relationship(std::string start_name, std::string end_name, int mode = 0, std::string label = "");//建立一個邊,mode是有向圖和無向圖兩種,label是邊上的標籤
int Create_Relationship(int start_id, int end_id, int mode = 0, std::string label = "");//根據節點編號建立邊,仍然是方便上層開發
void End();//表示這個圖畫完了
void Print_Dot(std::string file_path);//把.dot檔案生成到指定的目錄下,方便使用和人工更改。file_path最好是以".dot"結尾。
void Get_Png(std::string file_path);//畫圖,file_path以png結尾,表示最後輸出的圖片目錄
};
//DrawGraph.cpp
#include "DrawGraph.h"
void DrawGraph::Start(int mode)
{
FILE* fp = fopen(temp_file.c_str(), "w");
if (mode == DiGraph)
fprintf(fp, "digraph my_graph{\n");
else if (mode == Graph)
fprintf(fp, "graph my_graph{\n");
fclose(fp);
}
int DrawGraph::Create_Node(std::string node_name)
{
FILE* fp = fopen(temp_file.c_str(), "a");
str2int[node_name] = ++node_cnt;
int2str[node_cnt] = node_name;
fprintf(fp, "\t%s;\n", node_name.c_str());
fclose(fp);
return node_cnt;
}
int DrawGraph::Create_Node(int node_name)
{
std::string str_node_name = std::to_string(node_name);
return Create_Node(str_node_name);
}
int DrawGraph::Find_Node(std::string node_name)
{
if (str2int.find(node_name) == str2int.end())
return -1;
return str2int[node_name];
}
std::string DrawGraph::Find_Node(int node_id)
{
if (node_id > node_cnt || node_id <= 0)
return std::string();
return int2str[node_id];
}
int DrawGraph::Create_Relationship(std::string start_name, std::string end_name, int mode, std::string label)
{
FILE* fp = fopen(temp_file.c_str(), "a");
fprintf(fp, "\t%s ", start_name.c_str());
if (mode == DiGraph) {
fprintf(fp, " -> ");
}
else if (mode == Graph) {
fprintf(fp, " -- ");
}
fprintf(fp, " %s ", end_name.c_str());
if (label != std::string("")) {
fprintf(fp, "[label=%s]", label.c_str());
}
fprintf(fp, ";\n");
fclose(fp);
return ++rela_cnt;
}
int DrawGraph::Create_Relationship(int start_id, int end_id, int mode, std::string label)
{
if (Find_Node(start_id) == std::string() || Find_Node(end_id) == std::string())
return -1;
return Create_Relationship(Find_Node(start_id), Find_Node(end_id), mode, label);
}
void DrawGraph::End()
{
FILE* fp = fopen(temp_file.c_str(), "a");
fprintf(fp, "}");
fclose(fp);
}
void DrawGraph::Print_Dot(std::string file_path)
{
std::ifstream in(temp_file.c_str());
std::ofstream out(file_path.c_str());
out << in.rdbuf();
in.close();
out.close();
}
void DrawGraph::Get_Png(std::string file_path)
{
std::string command = "dot -Tpng " + temp_file + " -o " + file_path;
system(command.c_str());
}