C++版grpahviz库

两个前传

问题背景

课设非要用GUI,我一个主打性能的数据库也要做GUI。然而又不会用WEB,小程序那些,只能用我会的graphviz写了。但是graphviz好像没有C++结合的库?难顶

经过一番网络查找,发现GraphViz竟然有python的库?Graphviz竟然没有C/C++的库?

那咋办嘛?那咋办嘛?那咋办嘛?

想起之前的想用Graphviz做流程图,可惜学艺不精忘了rank机制和DAG建模不太行。这次用同样的思路:利用C++代码写dot文件,最后通过cmd的dot指令完成图片的生成!似乎可行。

所以本次开发的目的主要是想模仿Python的graphviz库的模型,开发DiGraph、Graph两个类,并且在两个类中实现多重维度的对点和关系的操纵和布置。其实是为了完成课设

主要功能

  1. 创建有向图/无向图
  2. 创建节点,并对节点的关系进行设定
  3. 查找已有节点的内部编号
  4. 更改已有节点的信息
  5. 创建关系,根据图的不同确定不同属性,并设定其属性取值
  6. 更改已有关系的属性
  7. 查看生成的dot代码
  8. 生成png文件

Futrue Work

  1. 增删改查
  2. 把每个属性可能的取值做成常量
  3. 动态图
  4. 多种布局命令
  5. 可以分别更改属性

应用实例

#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());
}

Reference

语雀-Graphviz教程

Graphviz官网

Graphviz文献集

Graphviz源码

Python-Graphviz

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值