两个前传
问题背景
课设非要用GUI,我一个主打性能的数据库也要做GUI。然而又不会用WEB,小程序那些,只能用我会的graphviz写了。但是graphviz好像没有C++结合的库?难顶
经过一番网络查找,发现GraphViz竟然有python的库?Graphviz竟然没有C/C++的库?
那咋办嘛?那咋办嘛?那咋办嘛?
想起之前的想用Graphviz做流程图,可惜学艺不精忘了rank机制和DAG建模不太行。这次用同样的思路:利用C++代码写dot文件,最后通过cmd的dot指令完成图片的生成!似乎可行。
所以本次开发的目的主要是想模仿Python的graphviz库的模型,开发DiGraph、Graph两个类,并且在两个类中实现多重维度的对点和关系的操纵和布置。其实是为了完成课设
主要功能
- 创建有向图/无向图
- 创建节点,并对节点的关系进行设定
- 查找已有节点的内部编号
- 更改已有节点的信息
- 创建关系,根据图的不同确定不同属性,并设定其属性取值
- 更改已有关系的属性
- 查看生成的dot代码
- 生成png文件
Futrue Work
- 增删改查
- 把每个属性可能的取值做成常量
- 动态图
- 多种布局命令
- 可以分别更改属性
应用实例
#include "DrawGraph.h"
int main() {
DiGraph d("test graph");
int n1 = d.Create_Node("TEST1", "red");
int n2 = d.Create_Node("TEST2", "blue");
d.Create_Node("TEST3", "black", "green");
int n3 = d.Find_Node("TEST3");
d.Create_Relationship(n1, n2, "knows", "orange");
d.Create_Relationship(n1, n3, "teach");
d.Create_Relationship("TEST2", "TEST3", "teach");
d.View("H:/graph.dot");
d.Tpng("H:/");
getchar();
d.Change_Node(n1, "TEST1_changed", "blue", "", "", "", 20, "box");
d.View("H:/graph2.dot");
d.Tpng("H:/");
}
第一次生成的图片↓
第二次生成的图片↓
Code
DrawGraph.h
//DrawGraph.h
#pragma once
#include <map>
#include <string>
#include <fstream>
#include <vector>
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);
int Create_Node(std::string 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 = "");
int Create_Relationship(int start_id, int end_id, int mode = 0, std::string label = "");
void End();
void Print_Dot(std::string file_path);
void Get_Png(std::string file_path);
};
class _Graph {
private:
struct Node
{
std::string label, color, fontcolor, fillcolor, fontname, shape, style;
int id, fontsize, width, height, deleted;
Node(int id, std::string label, std::string color = "", std::string fontcolor = "",
std::string fillcolor = "", std::string fontname = "", int fontsize = -1,
std::string shape = "", std::string style = "", int width = -1, int height = -1) {
this->id = id;
this->label = label;
this->color = color;
this->fontcolor = fontcolor;
this->fillcolor = fillcolor;
this->fontname = fontname;
this->fontsize = fontsize;
this->shape = shape;
this->style = style;
this->width = width;
this->height = height;
this->deleted = 0;
};
void Fprint(FILE* fp) {
fprintf(fp, "\t%d [label=%s", id, label.c_str());
if (color != "") fprintf(fp, ",color=%s", color.c_str());
if (fontcolor != "") fprintf(fp, ",fontcolor=%s", fontcolor.c_str());
if (fillcolor != "") fprintf(fp, ",fillcolor=%s", fillcolor.c_str());
if (fontname != "") fprintf(fp, ",fontname=%s", fontname.c_str());
if (fontsize != -1) fprintf(fp, ",fontsize=%d", fontsize);
if (shape != "") fprintf(fp, ",shape=%s", shape.c_str());
if (style != "") fprintf(fp, ",style=%s", style.c_str());
if (width != -1) fprintf(fp, ",width=%d", width);
if (height != -1) fprintf(fp, ",height=%d", height);
fprintf(fp, "];\n");
}
};
struct Relationship {
std::string label, color, fontcolor, fontname, style, dir, headlabel, taillabel;
int type, id, start_id, end_id, weight, fontsize, deleted, penwidth;
Relationship(int type, int id, int start_id, int end_id, std::string label = "", std::string color = "",
std::string fontcolor = "", std::string fontname = "", int fontsize = -1, std::string style = "",
std::string dir = "", std::string headlabel = "", std::string taillabel = "", int weight = -1, int penwidth = -1) {
this->type = type;
this->id = id;
this->start_id = start_id;
this->end_id = end_id;
this->label = label;
this->color = color;
this->fontcolor = fontcolor;
this->fontname = fontname;
this->fontsize = fontsize;
this->style = style;
this->dir = dir;
this->headlabel = headlabel;
this->taillabel = taillabel;
this->weight = weight;
this->penwidth = penwidth;
this->deleted = 0;
}
void Fprint(FILE* fp) {
if (type == 0) fprintf(fp, "\t%d -- %d [", start_id, end_id);
else fprintf(fp, "\t%d -> %d [", start_id, end_id);
if (label != "") fprintf(fp, "label=%s", label.c_str());
if (color != "") fprintf(fp, ",color=%s", color.c_str());
if (fontcolor != "") fprintf(fp, ",fontcolor=%s", fontcolor.c_str());
if (fontname != "") fprintf(fp, ",fontname=%s", fontname.c_str());
if (fontsize != -1) fprintf(fp, ",fontsize=%d", fontsize);
if (style != "") fprintf(fp, ",style=%s", style.c_str());
if (dir != "") fprintf(fp, ",dir=%s", dir.c_str());
if (headlabel != "") fprintf(fp, ",headlabel=%s", headlabel.c_str());
if (taillabel != "") fprintf(fp, ",taillabel=%s", taillabel.c_str());
if (weight != -1) fprintf(fp, ",weight=%d", weight);
if (penwidth != -1) fprintf(fp, ",penwidth=%d", penwidth);
fprintf(fp, "];\n");
}
};
std::map<std::string, int> str2int;
std::vector<std::string> int2str;
std::string temp_file = "./_graph_temp.tp";
std::string graph_name;
int node_cnt = 0, relationship_cnt = 0, mode = 0;
std::vector<Node> our_node;
std::vector<Relationship> our_relationship;
public:
static const int DIGRAPH = 1, GRAPH = 0;
_Graph(int mode, std::string graphname);
void Help();
int Create_Node(std::string label, std::string color = "", std::string fontcolor = "",
std::string fillcolor = "", std::string fontname = "", int fontsize = -1,
std::string shape = "", std::string style = "", int width = -1, int height = -1);
int Create_Node(int label, std::string color = "", std::string fontcolor = "",
std::string fillcolor = "", std::string fontname = "", int fontsize = -1,
std::string shape = "", std::string style = "", int width = -1, int height = -1);
int Find_Node(std::string name);
std::string Find_Node(int id);
int Create_Relationship(int start_id, int end_id, std::string label = "", std::string color = "",
std::string fontcolor = "", std::string fontname = "", int fontsize = -1, std::string style = "",
std::string dir = "", std::string headlabel = "", std::string taillabel = "", int weight = -1, int penwidth = -1);
int Create_Relationship(std::string start_name, std::string end_name, std::string label = "", std::string color = "",
std::string fontcolor = "", std::string fontname = "", int fontsize = -1, std::string style = "",
std::string dir = "", std::string headlabel = "", std::string taillabel = "", int weight = -1, int penwidth = -1);
bool Change_Node(int id, std::string label, std::string color = "", std::string fontcolor = "",
std::string fillcolor = "", std::string fontname = "", int fontsize = -1,
std::string shape = "", std::string style = "", int width = -1, int height = -1);
bool Change_Relationship(int id, int start_id, int end_id, std::string label = "", std::string color = "",
std::string fontcolor = "", std::string fontname = "", int fontsize = -1, std::string style = "",
std::string dir = "", std::string headlabel = "", std::string taillabel = "", int weight = -1, int penwidth = -1);
bool Change_Relationship(int id, std::string start_name, std::string end_name, std::string label = "", std::string color = "",
std::string fontcolor = "", std::string fontname = "", int fontsize = -1, std::string style = "",
std::string dir = "", std::string headlabel = "", std::string taillabel = "", int weight = -1, int penwidth = -1);
void View(std::string filepath);
void Tpng(std::string folderpath);
};
class DiGraph : public _Graph
{
public:
DiGraph(std::string graphname) : _Graph(1, graphname) {
return;
}
};//目前只实现了套壳
class Graph :public _Graph
{
public:
Graph(std::string graphname) : _Graph(0, graphname) {
return;
}
};//目前只实现了套壳
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());
}
_Graph::_Graph(int mode, std::string graphname)
{
this->mode = mode;
this->graph_name = graph_name;
}
void _Graph::Help()
{
printf("Ready to Add some show of the values of each data.");
}
int _Graph::Create_Node(std::string label, std::string color, std::string fontcolor,
std::string fillcolor, std::string fontname, int fontsize, std::string shape, std::string style, int width, int height)
{
int node_num = node_cnt++;
Node node_now(node_num, label, color, fontcolor, fillcolor, fontname, fontsize, shape, style, width, height);
our_node.push_back(node_now);
str2int[label] = node_num;
int2str.push_back(label);
return node_num;
}
int _Graph::Create_Node(int label, std::string color, std::string fontcolor, std::string fillcolor,
std::string fontname, int fontsize, std::string shape, std::string style, int width, int height)
{
std::string node_name = std::to_string(label);
return Create_Node(node_name, color, fontcolor, fillcolor, fontname, fontsize, shape, style, width, height);
}
int _Graph::Find_Node(std::string name)
{
if (str2int.find(name) == str2int.end())
return -1;
return str2int[name];
}
std::string _Graph::Find_Node(int id)
{
if (id >= int2str.size())
return int2str[id];
return std::string();
}
int _Graph::Create_Relationship(int start_id, int end_id, std::string label,
std::string color, std::string fontcolor, std::string fontname, int fontsize,
std::string style, std::string dir, std::string headlabel, std::string taillabel, int weight, int penwidth)
{
int relationship_num = relationship_cnt++;
Relationship relationship_now(mode, relationship_num,
start_id, end_id, label, color, fontcolor, fontname, fontsize, style, dir, headlabel, taillabel, weight, penwidth);
our_relationship.push_back(relationship_now);
return relationship_num;
}
int _Graph::Create_Relationship(std::string start_name, std::string end_name, std::string label,
std::string color, std::string fontcolor, std::string fontname, int fontsize,
std::string style, std::string dir, std::string headlabel, std::string taillabel, int weight, int penwidth)
{
int start_id = Find_Node(start_name), end_id = Find_Node(end_name);
if (start_id == -1 || end_id == -1) {
return -1;
}
return Create_Relationship(start_id, end_id, label, color, fontcolor, fontname, fontsize, style, dir, headlabel, taillabel, weight, penwidth);
}
bool _Graph::Change_Node(int id, std::string label, std::string color, std::string fontcolor,
std::string fillcolor, std::string fontname, int fontsize, std::string shape, std::string style, int width, int height)
{
if (id >= int2str.size())
return false;
Node node_now(id, label, color, fontcolor, fillcolor, fontname, fontsize, shape, style, width, height);
our_node[id] = node_now;
return true;
}
bool _Graph::Change_Relationship(int id, int start_id, int end_id, std::string label, std::string color,
std::string fontcolor, std::string fontname, int fontsize, std::string style, std::string dir,
std::string headlabel, std::string taillabel, int weight, int penwidth)
{
if (id >= our_relationship.size())
return false;
Relationship relationship_now(mode, id, start_id, end_id, label, color, fontcolor, fontname,
fontsize, style, dir, headlabel, taillabel, weight, penwidth);
our_relationship[id] = relationship_now;
return true;
}
bool _Graph::Change_Relationship(int id, std::string start_name, std::string end_name,
std::string label, std::string color, std::string fontcolor, std::string fontname,
int fontsize, std::string style, std::string dir, std::string headlabel, std::string taillabel, int weight, int penwidth)
{
int start_id = Find_Node(start_name), end_id = Find_Node(end_name);
if (start_id == -1 || end_id == -1) {
return false;
}
return Change_Relationship(id, start_id, end_id, label, color, fontcolor, fontname, fontsize, style, dir, headlabel, taillabel, weight, penwidth);
}
void _Graph::View(std::string filepath)
{
FILE* fp = fopen(filepath.c_str(), "w");
if (!fp) return;
if (mode == 0)
fprintf(fp, "graph %s{\n", graph_name.c_str());
else
fprintf(fp, "digraph %s{\n", graph_name.c_str());
for (int i = 0; i < our_node.size(); ++i)
our_node[i].Fprint(fp);
for (int i = 0; i < our_relationship.size(); ++i)
our_relationship[i].Fprint(fp);
fprintf(fp, "}");
fclose(fp);
}
void _Graph::Tpng(std::string folderpath)
{
FILE* fp = fopen(temp_file.c_str(), "w");
if (!fp) return;
if (mode == 0)
fprintf(fp, "graph %s{\n", graph_name.c_str());
else
fprintf(fp, "digraph %s{\n", graph_name.c_str());
for (int i = 0; i < our_node.size(); ++i)
our_node[i].Fprint(fp);
for (int i = 0; i < our_relationship.size(); ++i)
our_relationship[i].Fprint(fp);
fprintf(fp, "}");
fclose(fp);
std::string command = "dot -Tpng " + temp_file + " -o " + folderpath + "/output.png";
system(command.c_str());
}