Python使用graphviz绘制模块间数据流

news2025/1/12 17:46:40

graphviz官方参考链接:
http://www.graphviz.org/documentation/
https://graphviz.readthedocs.io/en/stable/index.html


文章目录

    • 需求描述
    • 环境配置
    • 实现思路
    • 代码实现


需求描述

根据各模块之间的传参关系绘制出数据流,如下图所示:
在这里插入图片描述
并且生成对应的graphviz代码:

digraph my_graph {
        Input [fillcolor=gray70 shape=box style=filled]
        Output [fillcolor=gray70 shape=box style=filled]
        NodeA
        NodeB
        NodeC
        Input -> NodeA [label=0]
        Input -> NodeA [label=1]
        NodeA -> NodeB [label=0]
        NodeA -> NodeC [label=1]
        NodeB -> Output [label=0]
        NodeC -> Output [label=0]
}

环境配置

  • 安装Python中需要使用的graphviz包:
pip install graphviz
  • 安装graphviz工具(可选,如果不安装无法直接使用Python的graphviz包导出图片),例如ubuntu系统安装指令如下,其他系统可参考官方文档https://www.graphviz.org/download/:
sudo apt install graphviz
  • VSCODE安装Graphviz Interactive Preview插件(可选,如果使用vscode开发建议安装此插件,通过此插件可以直接可视化graphviz代码,并保存图片)
    在这里插入图片描述

实现思路

实现一个Node基类,所有的模块实现都继承自该基类。再实现一个Message基类,模块之间传递的数据都继承自该基类。然后在数据传递过程中记录流经的每个模块的名称以及数据的传递方向即可绘制出想要的数据流。


代码实现

下面给出了一个简易的实现方式:

import os
from graphviz import Digraph


__graph_dict__ = {}


class Message:
    def __init__(self, node_name: str, idx: int):
        self.node_name = node_name
        self.idx = idx


class EdgeInfo:
    def __init__(self, start_node_name: str, end_node_name: str, label: str) -> None:
        self.start_node_name = start_node_name
        self.end_node_name = end_node_name
        self.label = label

    def __str__(self):
        return f'{self.start_node_name} -> {self.end_node_name} [label="{self.label}"];'


class Node:
    input_num: int
    output_num: int
    node_name: str

    def __call__(self, *args):
        global __graph_dict__
        assert len(args) == self.input_num

        if self.node_name not in __graph_dict__:
            __graph_dict__[self.node_name] = []

        for input_ in args:
            __graph_dict__[input_.node_name].append(EdgeInfo(input_.node_name,
                                                             self.node_name,
                                                             str(input_.idx)))

        res = tuple(Message(self.node_name, i) for i in range(self.output_num))
        if self.output_num == 1:
            return res[0]
        return res


def export_graphviz(graph, num_input: int, save_path: str):
    base_name = os.path.basename(save_path)
    name, _ = base_name.split(".")
    global __graph_dict__
    __graph_dict__.clear()
    __graph_dict__.update({"Input": [], "Output": []})

    # infer and collect flow info
    input_args = tuple(Message("Input", i) for i in range(num_input))
    outputs = graph(*input_args)
    for ouput_ in outputs:
        if ouput_.node_name not in __graph_dict__:
            __graph_dict__[ouput_.node_name] = []

        __graph_dict__[ouput_.node_name].append(EdgeInfo(ouput_.node_name,
                                                         "Output",
                                                         str(ouput_.idx)))

    # create graph code
    digraph = Digraph(name=name, format="jpg")

    # add nodes
    keys = list(__graph_dict__.keys())
    for k in keys:
        if k in ["Input", "Output"]:
            digraph.node(k, **{"shape": "box", "style": "filled", "fillcolor": "gray70"})
        else:
            digraph.node(k)

    # add edges
    for k in keys:
        for edge_info in __graph_dict__[k]:
            digraph.edge(edge_info.start_node_name,
                         edge_info.end_node_name,
                         edge_info.label)

    # print digraph code
    print(digraph.source)

    # export gv and jpg file
    try:
        digraph.render(directory=os.path.dirname(save_path))
    except Exception as e:
        print(f"export digraph failed, {e}")


class NodeA(Node):
    def __init__(self):
        self.input_num = 2
        self.output_num = 2
        self.node_name = "NodeA"


class NodeB(Node):
    def __init__(self):
        self.input_num = 1
        self.output_num = 1
        self.node_name = "NodeB"


class NodeC(Node):
    def __init__(self):
        self.input_num = 1
        self.output_num = 1
        self.node_name = "NodeC"


class Graph:
    def __init__(self):
        self.node_a = NodeA()
        self.node_b = NodeB()
        self.node_c = NodeC()

    def __call__(self, x0, x1):
        y0, y1 = self.node_a(x0, x1)
        z0 = self.node_b(y0)
        z1 = self.node_c(y1)

        return z0, z1


if __name__ == "__main__":
    graph = Graph()
    export_graphviz(graph, num_input=2, save_path="./my_graph.gv")

执行上述代码后会生成my_graph.gv以及my_graph.gv.jpg两个文件(如果没有安装graphviz工具是不会生成的),其中my_graph.gv是graphviz的代码形式,my_graph.gv.jpg是可视化后的结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1401567.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis(01)——常用指令

基础指令 select 数字:切换到其他数据库flushdb:清空当前数据库flushall:清空所有数据库dbsize:查看数据库大小exists key1[key2 …]:判断当前的key是否存在keys *:查看所有的keyexpire key 时间&#xff…

深入Matplotlib:画布分区与高级图形展示【第33篇—python:Matplotlib】

文章目录 Matplotlib画布分区技术详解引言方法一:plt.subplot()方法二:简略写法方法三:plt.subplots()实例展示添加更多元素 进一步探索Matplotlib画布分区自定义子图布局3D子图结语 Matplotlib画布分区技术详解 引言 Matplotlib是一个强大…

第13节-简历中的开放性问题

(点击即可收听) 不少公司的开放式题目每年不会有太大的变化,所以在答题前可先去相关求职论坛看看这些公司往年的问题,分析和思考自己应当怎么回答 开放式问题回答技巧 开放式问题主要考察的是求职者的求职动机、解决问题的能力、创造力等软实力&#xff…

C++入门之基础语法

目录 一.关键字 二.命名空间 2.1命名空间域 2.2展开命名空间域 using namespace bit 使用using将命名空间中的某个成员引入 2.3 头文件#include 2.3.1 头文件的展开和命名空间的展开区别 2.4 C的标准的库命名空间std 2.5 命名空间的套娃 三. C输入输出 3.1 流插入co…

线性代数:逆矩阵

目录 伴随阵 逆矩阵 证明:AA* A*A |A|E 证明:|A| 0 > |A*| 0 伴随阵 逆矩阵 证明:AA* A*A |A|E 证明:|A| 0 > |A*| 0

项目管理该考哪个证书❓NPDP还是软考❓

有小伙伴在纠结是要考NPDP认证呢还是考软考呢❓ 今天小编要给大家好好说说NPDP认证❗️ 💡NPDP全称New Product Development Professional,也就是产品经理国际资格认证。 🔥NPDP是国际公认的为一的新产品开发专业认证,是集理论、方…

JVM系列-2.字节码文件详解

👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家📕系列专栏:Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理🔥如果感觉博主的文…

(超详细)6-YOLOV5改进-添加ECA注意力机制

1、在yolov5/models下面新建一个EfficientChannelAttention.py文件,在里面放入下面的代码 代码如下: import torch, math from torch import nnclass EfficientChannelAttention(nn.Module): # Efficient Channel Attention moduledef __ini…

uniCloud uni-id体系的使用

目录 简介 uni-id导入和配置 用户表与文章表关联foreignKey 字段级权限控制 指定数据集权限控制 权限规则的变量和运算符 简介 uni-id已完成的功能: 注册、登录、发送短信验证码、密码加密保存、修改密码、忘记密码、头像管理、token管理、rbac权限角色体系、…

CVPR 2023 Hybrid Tutorial: All Things ViTs之mean attention distance (MAD)

All Things ViTs系列讲座从ViT视觉模型注意力机制出发,本文给出mean attention distance可视化部分阅读学习体会. 课程视频与课件: https://all-things-vits.github.io/atv/ 代码: https://colab.research.google.com/github/all-things-vits/code-samples/blob/main/probing/m…

2024年【河北省安全员B证】最新解析及河北省安全员B证试题及解析

题库来源:安全生产模拟考试一点通公众号小程序 河北省安全员B证最新解析是安全生产模拟考试一点通生成的,河北省安全员B证证模拟考试题库是根据河北省安全员B证最新版教材汇编出河北省安全员B证仿真模拟考试。2024年【河北省安全员B证】最新解析及河北省…

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-热门标签推荐显示实现

锋哥原创的SpringbootLayui python222网站实战: python222网站实战课程视频教程(SpringBootPython爬虫实战) ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程(SpringBootPython爬虫实战) ( 火…

【Spring 篇】MyBatis注解开发:编写你的数据乐章

欢迎来到MyBatis的音乐殿堂!在这个充满节奏和韵律的舞台上,注解是我们编写数据乐章的得力助手。无需繁琐的XML配置,通过简单而强大的注解,你将能够轻松地与数据库交互。在这篇博客中,我们将深入探讨MyBatis注解开发的精…

5G_射频测试_发射机测量(四)

6.2 Base station output power 用于测量载波发射功率的大小,功率越大小区半径越大但是杂散也会越大 载波功率(用频谱仪测)天线口功率(用功率计测)载波功率是以RBW为单位的filter测量的积分功率不同带宽的多载波测试时…

一文读懂「RAG,Retrieval-Augmented Generation」检索增强生成

Retrieval-Augmented Generation(RAG)作为机器学习和自然语言处理领域的一大创新,不仅代表了技术的进步,更在实际应用中展示了其惊人的潜力。 RAG结合了检索(Retrieval)和生成(Generation&#…

项目解决方案:多地医馆的高清视频监控接入汇聚联网

目 录 一、背景 二、建设目标及需求 1.建设目标 2.现状分析 3.需求分析 三、方案设计 1.设计依据 2.设计原则 3.方案设计 3.1 方案描述 3.2 组网说明 四、产品介绍 1.视频监控综合资源管理平台介绍 2.视频录像服务器和存储 2.1概述 2.2存储设计 …

蓝桥杯省赛无忧 编程9

#include<bits/stdc.h> using namespace std; int main() {int n,k,ans0;cin>>n>>k;while(n--){int a;cin>>a;ansa&1;}if(ans&1) cout<<"Alice"<<\n;else cout<<"Bob"; return 0; }这个游戏是基于数…

软件工程应用题汇总

绘制数据流图(L0/L1/L2) DFD/L0&#xff08;基本系统模型&#xff09; 只包含源点终点和一个处理(XXX系统) DFD/L1&#xff08;功能级数据流图&#xff09;在L0基础上进一步划分处理(XXX系统) 个人理解 DFD/L2&#xff08;在L1基础上进一步分解后的数据流图&#xff09; 数据…

3.php开发-个人博客项目输入输出类留言板访问IPUA头来源

目录 知识点 : 输入输出 配置环境时&#xff1a; 搜索框&#xff1a; 留言板&#xff1a; 留言板的显示&#xff08;html&#xff09;&#xff1a; php代码显示提交的留言&#xff1a; 写入数据库 对留言内容进行显示&#xff1a; php全局变量-$_SERVER 检测来源 墨…

【复现】Hytec Inter HWL 2511 SS路由器RCE漏洞_25

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 Hytec Inter HWL 2511 SS是日本Hytec Inter 公司的一款工业级 LTE 路由器&#xff0c;可用于远程数据传输&#xff0c;例如收集传…