如何在Graphviz Python中让箭头从左侧进入注释节点?
解决Graphviz Python中注释节点的箭头接入方向问题
需求说明
我用Graphviz的Python库绘图,有个特定需求:标记在annotations列表里的半矩形样式注释节点,需要让箭头从左侧进入,而非默认的底部。
代码背景
model对象包含edge对象,每个edge有source和target节点(本质是字符串)- 维护
annotations列表,用来标记需要设置为半矩形样式的节点 - 核心目标:让这类节点的入箭头改为从左侧接入
原代码
g = Digraph('G', filename='./output/model_' + str(mi), format='png', strict=True) g.graph_attr.update(rankdir='BT') g.attr('node', shape='note') sources = list() targets = list() annotations = list() PossibleModel.MAX_DEPTH = 3 """Let us first see what the possible inputs are""" """ for edge in model.get_edges(): sources.append(str(edge.source)) targets.append(str(edge.target)) """ for edge in model.get_edges(): sources.append(edge.source.small_string2()) targets.append(edge.target.small_string2()) difference = list(set(sources) - set(targets)) print('difference', difference) for edge in model.get_edges(): edge_contained = '' for rule in br_rules: if str(edge.source) in rule or str(edge.target) in rule: edge_contained += rule + '\n' if edge_contained == '': g.edge(edge.source.small_string2(), edge.target.small_string2(), label=str(edge.weight), color='red') g.edge(edge.source.small_string2(), edge.source.small_string3(), color='black', arrowhead='none', style='dashed') g.edge(edge.target.small_string2(), edge.target.small_string3(), color='black', arrowhead='none', style='dashed') annotations.append(edge.source.small_string3()) annotations.append(edge.target.small_string3()) else: g.attr('edge', color='blue') g.edge(str(edge.source), str(edge.target), label=edge_contained) for node in difference: g.node(node, style='rounded', shape='ellipse') for node in targets: g.node(node, shape='box') for node in annotations: g.node(node, color='transparent', image='test.png', fontsize='11')
解决方案
要实现箭头从左侧接入注释节点,核心是利用Graphviz的**端口(port)**特性,指定边连接到节点的左侧端口(west)。具体修改如下:
- 修改指向注释节点的边:在创建指向
annotations列表中节点的边时,把目标节点格式改为{节点名}:west,强制箭头连接到节点左侧。 - (可选)调整节点锚点:如果使用自定义图片作为节点样式,可以设置
imagepos="ml"(middle left),让图片的左侧作为端口锚点,确保箭头位置准确。
修改后的关键代码片段
# 原代码中创建指向注释节点的边部分,替换为: g.edge(edge.source.small_string2(), f"{edge.source.small_string3()}:west", color='black', arrowhead='none', style='dashed') g.edge(edge.target.small_string2(), f"{edge.target.small_string3()}:west", color='black', arrowhead='none', style='dashed') # 注释节点的设置可添加imagepos属性(如果需要): for node in annotations: g.node(node, color='transparent', image='test.png', fontsize='11', imagepos="ml")
完整修改后的代码
g = Digraph('G', filename='./output/model_' + str(mi), format='png', strict=True) g.graph_attr.update(rankdir='BT') g.attr('node', shape='note') sources = list() targets = list() annotations = list() PossibleModel.MAX_DEPTH = 3 """Let us first see what the possible inputs are""" """ for edge in model.get_edges(): sources.append(str(edge.source)) targets.append(str(edge.target)) """ for edge in model.get_edges(): sources.append(edge.source.small_string2()) targets.append(edge.target.small_string2()) difference = list(set(sources) - set(targets)) print('difference', difference) for edge in model.get_edges(): edge_contained = '' for rule in br_rules: if str(edge.source) in rule or str(edge.target) in rule: edge_contained += rule + '\n' if edge_contained == '': g.edge(edge.source.small_string2(), edge.target.small_string2(), label=str(edge.weight), color='red') # 修改这里,指定目标节点的west端口 g.edge(edge.source.small_string2(), f"{edge.source.small_string3()}:west", color='black', arrowhead='none', style='dashed') g.edge(edge.target.small_string2(), f"{edge.target.small_string3()}:west", color='black', arrowhead='none', style='dashed') annotations.append(edge.source.small_string3()) annotations.append(edge.target.small_string3()) else: g.attr('edge', color='blue') g.edge(str(edge.source), str(edge.target), label=edge_contained) for node in difference: g.node(node, style='rounded', shape='ellipse') for node in targets: g.node(node, shape='box') # 可选:添加imagepos确保图片锚点在左侧 for node in annotations: g.node(node, color='transparent', image='test.png', fontsize='11', imagepos="ml")
原理说明
Graphviz的节点支持通过:方向格式指定连接端口,常用的方向包括:
west:左侧east:右侧north:顶部south:底部
因为你的图设置了rankdir='BT'(从下到上布局),默认箭头会沿着布局方向从底部接入节点,指定:west后,强制边连接到节点的左侧,满足需求。
内容的提问来源于stack exchange,提问作者Alexandre Goossens
相关产品推荐
相关产品推荐

