使用networkx生成绘制节点的原始数据,使用matplotlib.pyplot进行绘制,使用了canvas.mpl_connect鼠标事件绑定,使图形根据数据更新动起来,直接上代码结果!鼠标左键点击新增节点;右键点击删除该节点下的其他节点,保留主节点;中间点击显示该节点详细信息。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time : 2022/10/2 16:51
# @Author : singweek
# @File: Plt_Tree
import networkx as nx
import matplotlib.pyplot as plt
"""创建图"""
# G=nx.Graph()#无多重边无向图。忽略两个节点之间的多个边,允许节点自身成环。
# G = nx.DiGraph()#无多重边有向图
# G=nx.MultiGraph()#有多重边无向图,允许在成对的节点之间存在多个无向边。
# G=nx.MultiDiGraph()#有多重边有向图
# G = nx.davis_southern_women_graph()#构造关系图
"""新增节点边界以及删除节点边界"""
# G.add_node("start", subset=0)#添加节点
# G.add_nodes_from([1,2,3,4,5],weight=1)
# G.add_edges_from([('start','2'),('start','3')])#更新多个边界新信息,新增连接
# G.add_edge(('3', '5'))#更新单个边界信息
# G.remove_edges_from([('start','2'),('start','3')])#删除多个边界信息,start-2
# G.remove_edge(('start','2'))#删除单个边界信息
# G.remove_node()
# G.remove_nodes_from()
"""绘制节点显示"""
# nx.draw(G, pos, with_labels=True, node_color=node_color_l, edge_color=edge_color, node_size=1000) # ,with_labels=True
# # 展示
# plt.show()
def Get_Address_info(address):
"""
获取显示的数据,自己根据需求修改
return address_info_dict={'address':"address info"}
"""
address_info_dict={'start':"address info",'2':'adress 2','3':'adress3'}
if address in address_info_dict.keys():
return address_info_dict[address]
else:
return ''
def Get_Address_Node(address):
"""
获取节点数据,可以动态加载,自己根据需求修改
return address_node_dict={"node_cur":"start","node_pre":[1,2],"node_next":[3,4],"node_pre_color":'g',"node_next_color":'b'}
"""
Data_dict={'start':{"node_cur":'start','node_pre':['1-0','2-0','3-0'],'node_next':['1-1','2','1-3'],"node_pre_color":'g',"node_next_color":'b'},
'2':{"node_cur":'2','node_pre':['1','2-1','2-2'],'node_next':['2-1','2-2','3'],"node_pre_color":'g',"node_next_color":'b'},
'3':{"node_cur":'3','node_pre':['3-1','3-3','3-2'],'node_next':['3-4','3-5','4'],"node_pre_color":'g',"node_next_color":'b'}}
# address_node_dict={"node_cur":"start","node_pre":[1,2],"node_next":[3,4],"node_pre_color":'g',"node_next_color":'b'}
if address in Data_dict.keys():
return Data_dict[address]
else:
return {}
# 鼠标点击响应函数
def on_press(event,loc,error_xy=0.05):
"""
鼠标点击事件判断
event.button 1、2、3分别为鼠标左键、中键、右键点击
event.x, event.y 显示屏幕的坐标,从左上角开始计算
event.xdata, event.ydata 绘图中的坐标,从左上角开始计算,0-1范围内
loc 传入所有节点的参数及坐标
error_xy 点击坐标误差
"""
global Node_Line
print("点击坐标:",'button=%d, x=%d, y=%d, xdata=%f, ydata=%f' %(event.button, event.x, event.y, event.xdata, event.ydata))
print("节点坐标",loc)
x,y=event.xdata, event.ydata
for i in loc.keys():
tx,ty=loc[i]
if tx-error_xy<=x<=tx+error_xy and ty-error_xy<=y<=ty+error_xy:
print("点击节点:",i)
if event.button==2:#鼠标中键显示节点信息
address_info=Get_Address_info(i)#获取节点信息,以便用于绘制{'1':'address'}
if address_info!="":
Plt_labels(i,address=address_info)
plt.show()
if event.button==1:#鼠标左键点击,扩张节点
node_cur_dict=Get_Address_Node(i)#获取节点信息,用于绘制
if node_cur_dict!={}:
node_cur = node_cur_dict['node_cur']
node_pre = node_cur_dict['node_pre']
node_next = node_cur_dict['node_next']
node_pre_color = node_cur_dict['node_pre_color']
node_next_color = node_cur_dict['node_next_color']
Node_Line.append(node_cur)
Add_Refresh_plt(node_cur,node_pre,node_next,node_pre_color=node_pre_color,node_next_color=node_next_color)
if event.button==3:#鼠标右键点击,删除节点
node_cur_dict=Get_Address_Node(i)#获取节点信息,用于绘制
if node_cur_dict != {}:
node_cur = node_cur_dict['node_cur']
node_pre = node_cur_dict['node_pre']
node_next = node_cur_dict['node_next']
node_pre_color = node_cur_dict['node_pre_color']
node_next_color = node_cur_dict['node_next_color']
Delete_Refresh_plt(node_cur, node_pre, node_next,node_pre_color=node_pre_color,node_next_color=node_next_color)
def Add_Refresh_plt(node_cur,node_pre=[],node_next=[],node_pre_color='green',node_next_color='blue',edge_color='black'):
"""
新增节点,刷新画布
:param node_cur开始点击的节点
:param node_pre点击的上一节点[1,2,3]传入当前地址的节点
:param node_next点击的下一节点[1,2,3]当前节点的输出节点
# :param node_color节点颜色{1:'r',2:'y'}
:param node_pre_color新增节点颜色'r'
:param node_next_color新增节点颜色'y'
:param edge_color边界颜色'black'默认黑色,所有边界颜色都一样
"""
global cid,node_color,Node_Line,pos
pos_o = nx.spring_layout(G) # 获取绘制前节点位置
# if node_cur=="start":#暂时定位start
node_color.update({node_cur: 'red'}) # 修改点击节点颜色,默认红色
"""新增上一节点"""
for i in node_pre:
if i not in pos_o.keys():#如果新增节点不在之前绘制的节点内
G.add_node(i)#新增节点
node_color.update({i:node_pre_color})#新增节点颜色
for i in node_pre:
G.add_edge(node_cur, i)
"""新增下一节点"""
for i in node_next:
if i not in pos_o.keys():#如果新增节点不在之前绘制的节点内
G.add_node(i)#新增节点
node_color.update({i: node_next_color}) # 新增节点颜色
for i in node_next:
G.add_edge(node_cur,i)
pos = nx.spring_layout(G) # 获取新增节点位置
fig.canvas.mpl_disconnect(cid)#解除鼠标事件绑定
plt.clf()#
cid=fig.canvas.mpl_connect('button_press_event', lambda event: on_press(event, pos))#鼠标事件绑定
node_color_l=[node_color[i] for i in node_color.keys()]
nx.draw(G, pos, with_labels=True, node_color=node_color_l, edge_color=edge_color, node_size=1000) #绘制
plt.show()#显示
def Delete_Refresh_plt(node_cur,node_pre=[],node_next=[],node_pre_color='green',node_next_color='blue',edge_color='black'):
"""
删除节点,刷新画布
:param node_cur开始点击的节点
:param node_pre点击的上一节点[1,2,3]传入当前地址的节点
:param node_next点击的下一节点[1,2,3]当前节点的输出节点
# :param node_color节点颜色{1:'r',2:'y'}
:param node_pre_color新增节点颜色'r'
:param node_next_color新增节点颜色'y'
:param edge_color边界颜色'black'默认黑色,所有边界颜色都一样
"""
global cid,node_color,Node_Line,pos
# pos_o = nx.spring_layout(G) # 获取绘制前节点位置
# if node_cur=="start":#暂时定位start
node_color.update({node_cur: 'red'}) # 修改点击节点颜色,默认红色
# print("删除节点",node_pre,node_next)
for i in node_next:#删除下一个节点
if i not in Node_Line:
try:
G.remove_node(i)
del node_color[i]
except:
print("删除出错节点:", i)
for i in node_pre:#删除上一个节点
if i not in Node_Line:
try:
G.remove_node(i)
del node_color[i]
except:
print("删除出错节点:", i)
pos = nx.spring_layout(G) #获取新增节点位置
fig.canvas.mpl_disconnect(cid)#解除鼠标事件绑定
plt.clf()#
cid=fig.canvas.mpl_connect('button_press_event', lambda event: on_press(event, pos))#鼠标事件绑定
node_color_l=[node_color[i] for i in node_color.keys()]
nx.draw(G, pos, with_labels=True, node_color=node_color_l, edge_color=edge_color, node_size=1000) #绘制
plt.show()#显示
def Plt_labels(node,address="address",error_xy=0.2,font_color='magenta'):
"""
绘制节点标签
# :param pos 坐标{'start': array([0.27709403, 0.38102859])}
:param node 节点名称
:param address 绘制参数
:param error_xy 绘制位置调整
:param font_color 'magenta'
"""
# pos = nx.spring_layout(G) # 获取新增节点位置
global pos
xy=pos[node]
pos_label={'start':[xy[0],xy[1]-error_xy]}
node_label={'start':address}
nx.draw_networkx_labels(G, pos_label, labels=node_label,font_color=font_color)
if __name__ == '__main__':
start_node="start"
Node_Line = [start_node]
G = nx.Graph() # 无多重边无向图。忽略两个节点之间的多个边,允许节点自身成环。
G.add_node(start_node)
pos = nx.spring_layout(G)#获取节点位置
node_color={'start':'red'}#起始节点颜色
node_color_l=[node_color[i] for i in node_color.keys()]
edge_color='black'
fig = plt.figure()
#绑定鼠标事件
cid=fig.canvas.mpl_connect('button_press_event',lambda event:on_press(event,pos))
# networkx画关系图
nx.draw(G, pos, with_labels=True,node_color=node_color_l, edge_color=edge_color, node_size=1000) # ,with_labels=True
# 展示
plt.show()