I created a directed graph using networkx.DiGraph
then plotted it using networkx.draw_spring(graph)
, so all the nodes of the graph have some details stored in a list of dictionaries.
How to add something like a "tooltip" to view these details on mouse hover on each node? If this is possible, how to make this "tooltip" always visible for all nodes, not just by hovering?
To label all the nodes, you just need to use annotate
. Something like this
import matplotlib.pyplot as plt
import networkx as nx
G = nx.path_graph(5)
attrs = {0: {'attr1': 20, 'attr2': 'nothing'}, 1: {'attr2': 3}, 2: {'attr1': 42}, 3: {'attr3': 'hello'}, 4: {'attr1': 54, 'attr3': '33'}}
nx.set_node_attributes(G, attrs)
nx.draw(G)
for node in G.nodes:
xy = pos[node]
annot.xy = xy
node_attr = G.nodes[node]
text = '\n'.join(f'{k}: {v}' for k, v in G.nodes[node].items())
text = f'node {node}\n' + text
ax.annotate(text, xy=xy)
Here's a working example of getting tooltip on hover. This is based off tooltips using standard matplotlib plots here . I used draw_networkx_nodes
to get the objects used for hovering and displaying tooltips instead of using draw_spring
. But you can manually define the position with spring_layout
.
import matplotlib.pyplot as plt
import networkx as nx
G = nx.path_graph(5)
attrs = {0: {'attr1': 20, 'attr2': 'nothing'}, 1: {'attr2': 3}, 2: {'attr1': 42}, 3: {'attr3': 'hello'}, 4: {'attr1': 54, 'attr3': '33'}}
nx.set_node_attributes(G, attrs)
fig, ax = plt.subplots()
pos = nx.spring_layout(G)
nodes = nx.draw_networkx_nodes(G, pos=pos, ax=ax)
nx.draw_networkx_edges(G, pos=pos, ax=ax)
annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
bbox=dict(boxstyle="round", fc="w"),
arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)
def update_annot(ind):
node = ind["ind"][0]
xy = pos[node]
annot.xy = xy
node_attr = {'node': node}
node_attr.update(G.nodes[node])
text = '\n'.join(f'{k}: {v}' for k, v in node_attr.items())
annot.set_text(text)
def hover(event):
vis = annot.get_visible()
if event.inaxes == ax:
cont, ind = nodes.contains(event)
if cont:
update_annot(ind)
annot.set_visible(True)
fig.canvas.draw_idle()
else:
if vis:
annot.set_visible(False)
fig.canvas.draw_idle()
fig.canvas.mpl_connect("motion_notify_event", hover)
plt.show()
FYI @busybear @ sudofix
This only works if you have your nodes starting from 0.
if you do:
import matplotlib.pyplot as plt
import networkx as nx
nodes = list(range(5))
edges = []
for e1,e2 in zip(nodes[:-1],nodes[1:]):
edges.append((e1,e2))
G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)
attrs = {}
for node in G.nodes:
attrs[node] = {'attr1': node, 'attr2': 'hello', 'attr3': 33}
and leave the rest as
nx.set_node_attributes(G, attrs)
fig, ax = plt.subplots()
pos = nx.spring_layout(G)
nodes = nx.draw_networkx_nodes(G, pos=pos, ax=ax)
nx.draw_networkx_edges(G, pos=pos, ax=ax)
annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
bbox=dict(boxstyle="round", fc="w"),
arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)
def update_annot(ind):
node = ind["ind"][0]
xy = pos[node]
annot.xy = xy
node_attr = {'node': node}
node_attr.update(G.nodes[node])
text = '\n'.join(f'{k}: {v}' for k, v in node_attr.items())
annot.set_text(text)
def hover(event):
vis = annot.get_visible()
if event.inaxes == ax:
cont, ind = nodes.contains(event)
if cont:
update_annot(ind)
annot.set_visible(True)
fig.canvas.draw_idle()
else:
if vis:
annot.set_visible(False)
fig.canvas.draw_idle()
fig.canvas.mpl_connect("motion_notify_event", hover)
plt.show()
everything works great.
But if you change nodes = list(range(5))
to be nodes = list(range(1,6))
it does not work, because node = ind["ind"][0]
returns the position of the nodes in G.nodes
and not the name of the node, so accessing pos[node]
and G.nodes[node]
gets the wrong position (it is shifted by 1).
The solution is to create a mapping like
idx_to_node_dict = {}
for idx, node in enumerate(G.nodes):
idx_to_node_dict[idx] = node
and fix the function to use it as:
def update_annot(ind):
node_idx = ind["ind"][0]
node = idx_to_node_dict[node_idx]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.