利用C++&Graphviz完成绘图任务

本文介绍了如何使用C++创建一个类DrawGraph,通过其成员函数绘制图形,支持有向图和无向图,以及节点属性和边属性的设置。详细步骤包括创建节点、关系和图形文件,以及导出PNG格式图片。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前传:Graphviz解决图论简单画图

问题背景

Graphviz是个好东西啊,但是可惜的是我并没有找到可以直接用C++画图的程序?(可能是我找的不对劲?

需求: 完成一个C++类,能够通过C++类中的函数调用等方法完成绘图工作。

前提: 已经完成Graphviz的安装,并且把对应的路径加入到系统的PATH中,即“前传”部分已经完成。

解决方案

  1. 完成一个C++画图类,类比python networkx那种,可以调用函数来画图。
  2. 创建一个临时文件,按照过程写入Graphviz的dot文件
  3. 能够区分有向图跟无向图
  4. 未完待续…

应用实例

//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

  1. 增加节点的多种属性设置,基本按照Graphviz的Attribute来设定
  2. 增加子图模式,允许在整张图之中增加子图。
  3. 增加边的多种属性模式,包括形状颜色等。
  4. 提供面向对象的建模方式,方便快速排版生成的dot文件。
  5. 允许对其中的节点进行层级排布,允许加入顺序关系。
  6. 支持html等格式的初始化节点。
  7. 支持从邻接矩阵、邻接表中导入图形。
  8. 未完待续…

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

参考文献&扩展阅读

Graphviz官网

Graphviz文献集

Graphviz源码

Python-Graphviz

语雀-Graphviz教程

WebGraphviz

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值