如何检测点击了 Matplotlib PathCollection 中的哪个项目?
How to detect which item in a Matplotlib PathCollection has been clicked on?
我可以使用 Matplotlib 绘制有向网络图。现在我希望能够响应鼠标事件,以便用户可以与网络进行交互。例如,一个节点可以在用户点击它时改变它的颜色。这只是一个简单的例子,但它说明了这一点。我还想知道点击了哪个节点(标签);我对它在 space.
中的 x,y 坐标并不感兴趣
到目前为止,这是我的代码:
import matplotlib.pyplot as plt
from matplotlib.collections import PathCollection
import networkx as nx
def createDiGraph():
G = nx.DiGraph()
# Add nodes:
nodes = ['A', 'B', 'C', 'D', 'E']
G.add_nodes_from(nodes)
# Add edges or links between the nodes:
edges = [('A','B'), ('B','C'), ('B', 'D'), ('D', 'E')]
G.add_edges_from(edges)
return G
G = createDiGraph()
# Get a layout for the nodes according to some algorithm.
pos = nx.layout.spring_layout(G, random_state=779)
node_size = 300
nodes = nx.draw_networkx_nodes(G, pos, node_size=node_size, node_color=(0,0,0.9),
edgecolors='black')
# nodes is a matplotlib.collections.PathCollection object
nodes.set_picker(5)
#nodes.pick('button_press_event')
#print(nodes.get_offsets())
nx.draw_networkx_edges(G, pos, node_size=node_size, arrowstyle='->',
arrowsize=15, edge_color='black', width=1)
nx.draw_networkx_labels(G, pos, font_color='red', font_family='arial',
font_size=10)
def onpick(event):
#print(event.mouseevent)
if isinstance(event.artist, PathCollection):
#nodes = event.artist
print (event.ind)
fig = plt.gcf()
# Bind our onpick() function to pick events:
fig.canvas.mpl_connect('pick_event', onpick)
# Hide the axes:
plt.gca().set_axis_off()
plt.show()
绘制时网络看起来像这样:
例如,如果我用鼠标单击节点 C,程序会打印出 [1]
;或节点 E 的 [3]
。请注意,索引不对应于 A 的 0、B 的 1、C 的 2,等等,即使原始节点已按该顺序添加到 networkx 有向图。
那么当我点击节点C时如何获取值'C'呢?以及如何获得图中代表 C 的对象以便更改其颜色?
我试过 PathCollection.pick
,但我不确定要将什么传递给它,而且我不确定这是否是正确的使用方法。
看起来 G
在创建时不保留节点的顺序,但是,节点的顺序似乎存储在 pos
中。我会推荐以下内容:
添加到您的导入语句:
from ast import literal_eval
在 nx.draw_networkx_nodes
中定义 label
并在函数 update_plot
中创建绘图,该函数将 G
、pos
和 color
作为参数:
def update_plot(pos, G, colors):
# the keys of the pos dictionary contains the labels that you are interested in
# or label = [*pos]
nodes = nx.draw_networkx_nodes(G, pos, node_size=node_size, node_color=colors,edgecolors='black', label=list(pos.keys()))
# nodes is a matplotlib.collections.PathCollection object
nodes.set_picker(5)
nx.draw_networkx_edges(G, pos, node_size=node_size, arrowstyle='->', arrowsize=15, edge_color='black', width=1)
nx.draw_networkx_labels(G, pos, font_color='red', font_family='arial', font_size=10)
你的 onpick
函数应该是:
def onpick(event):
if isinstance(event.artist, PathCollection):
#index of event
ind = event.ind[0]
#convert the label list from a string back to a list
label_list = literal_eval(event.artist.get_label())
print(label_list[ind])
colors = [(0, 0, 0.9)] * len(pos)
colors[ind]=(0.9,0,0)
update_plot(pos, G, colors)
你的代码主体就是:
G = createDiGraph()
# Get a layout for the nodes according to some algorithm.
pos = nx.layout.spring_layout(G, random_state=779)
node_size = 300
colors=[(0,0,0.9)]*len(pos)
update_plot(pos, G, colors)
在@apogalacticon 的有用评论后,我最终找到了这个解决方案。
import matplotlib.pyplot as plt
from matplotlib.collections import PathCollection
import networkx as nx
def createDiGraph():
G = nx.DiGraph()
# Add nodes:
nodes = ['A', 'B', 'C', 'D', 'E']
G.add_nodes_from(nodes)
# Add edges or links between the nodes:
edges = [('A','B'), ('B','C'), ('B', 'D'), ('D', 'E')]
G.add_edges_from(edges)
return G
G = createDiGraph()
# Get a layout for the nodes according to some algorithm.
pos = nx.layout.spring_layout(G, random_state=779)
node_size = 300
nodes = nx.draw_networkx_nodes(G, pos, node_size=node_size, node_color=(0,0,0.9),
edgecolors='black')
# nodes is a matplotlib.collections.PathCollection object
nodes.set_picker(5)
nx.draw_networkx_edges(G, pos, node_size=node_size, arrowstyle='->',
arrowsize=15, edge_color='black', width=1)
nx.draw_networkx_labels(G, pos, font_color='red', font_family='arial',
font_size=10)
def onpick(event):
if isinstance(event.artist, PathCollection):
all_nodes = event.artist
ind = event.ind[0] # event.ind is a single element array.
this_node_name = pos.keys()[ind]
print(this_node_name)
# Set the colours for all the nodes, highlighting the picked node with
# a different colour:
colors = [(0, 0, 0.9)] * len(pos)
colors[ind]=(0, 0.9, 0)
all_nodes.set_facecolors(colors)
# Update the plot to show the change:
fig.canvas.draw() # plt.draw() also works.
#fig.canvas.flush_events() # Not required? See
fig = plt.gcf()
# Bind our onpick() function to pick events:
fig.canvas.mpl_connect('pick_event', onpick)
# Hide the axes:
plt.gca().set_axis_off()
plt.show()
当您单击一个节点时,其表面颜色变为绿色,而其他节点设置为蓝色:
不幸的是,网络中的节点似乎由单个 matplotlib.artist.Artist
对象表示,该对象恰好是没有任何艺术家子节点的路径集合。这意味着您无法获取单个节点来更改其属性。相反,您被迫更新所有节点,只需确保所选节点的属性(在本例中为颜色)与其他节点不同。
我可以使用 Matplotlib 绘制有向网络图。现在我希望能够响应鼠标事件,以便用户可以与网络进行交互。例如,一个节点可以在用户点击它时改变它的颜色。这只是一个简单的例子,但它说明了这一点。我还想知道点击了哪个节点(标签);我对它在 space.
中的 x,y 坐标并不感兴趣到目前为止,这是我的代码:
import matplotlib.pyplot as plt
from matplotlib.collections import PathCollection
import networkx as nx
def createDiGraph():
G = nx.DiGraph()
# Add nodes:
nodes = ['A', 'B', 'C', 'D', 'E']
G.add_nodes_from(nodes)
# Add edges or links between the nodes:
edges = [('A','B'), ('B','C'), ('B', 'D'), ('D', 'E')]
G.add_edges_from(edges)
return G
G = createDiGraph()
# Get a layout for the nodes according to some algorithm.
pos = nx.layout.spring_layout(G, random_state=779)
node_size = 300
nodes = nx.draw_networkx_nodes(G, pos, node_size=node_size, node_color=(0,0,0.9),
edgecolors='black')
# nodes is a matplotlib.collections.PathCollection object
nodes.set_picker(5)
#nodes.pick('button_press_event')
#print(nodes.get_offsets())
nx.draw_networkx_edges(G, pos, node_size=node_size, arrowstyle='->',
arrowsize=15, edge_color='black', width=1)
nx.draw_networkx_labels(G, pos, font_color='red', font_family='arial',
font_size=10)
def onpick(event):
#print(event.mouseevent)
if isinstance(event.artist, PathCollection):
#nodes = event.artist
print (event.ind)
fig = plt.gcf()
# Bind our onpick() function to pick events:
fig.canvas.mpl_connect('pick_event', onpick)
# Hide the axes:
plt.gca().set_axis_off()
plt.show()
绘制时网络看起来像这样:
例如,如果我用鼠标单击节点 C,程序会打印出 [1]
;或节点 E 的 [3]
。请注意,索引不对应于 A 的 0、B 的 1、C 的 2,等等,即使原始节点已按该顺序添加到 networkx 有向图。
那么当我点击节点C时如何获取值'C'呢?以及如何获得图中代表 C 的对象以便更改其颜色?
我试过 PathCollection.pick
,但我不确定要将什么传递给它,而且我不确定这是否是正确的使用方法。
看起来 G
在创建时不保留节点的顺序,但是,节点的顺序似乎存储在 pos
中。我会推荐以下内容:
添加到您的导入语句:
from ast import literal_eval
在 nx.draw_networkx_nodes
中定义 label
并在函数 update_plot
中创建绘图,该函数将 G
、pos
和 color
作为参数:
def update_plot(pos, G, colors):
# the keys of the pos dictionary contains the labels that you are interested in
# or label = [*pos]
nodes = nx.draw_networkx_nodes(G, pos, node_size=node_size, node_color=colors,edgecolors='black', label=list(pos.keys()))
# nodes is a matplotlib.collections.PathCollection object
nodes.set_picker(5)
nx.draw_networkx_edges(G, pos, node_size=node_size, arrowstyle='->', arrowsize=15, edge_color='black', width=1)
nx.draw_networkx_labels(G, pos, font_color='red', font_family='arial', font_size=10)
你的 onpick
函数应该是:
def onpick(event):
if isinstance(event.artist, PathCollection):
#index of event
ind = event.ind[0]
#convert the label list from a string back to a list
label_list = literal_eval(event.artist.get_label())
print(label_list[ind])
colors = [(0, 0, 0.9)] * len(pos)
colors[ind]=(0.9,0,0)
update_plot(pos, G, colors)
你的代码主体就是:
G = createDiGraph()
# Get a layout for the nodes according to some algorithm.
pos = nx.layout.spring_layout(G, random_state=779)
node_size = 300
colors=[(0,0,0.9)]*len(pos)
update_plot(pos, G, colors)
在@apogalacticon 的有用评论后,我最终找到了这个解决方案。
import matplotlib.pyplot as plt
from matplotlib.collections import PathCollection
import networkx as nx
def createDiGraph():
G = nx.DiGraph()
# Add nodes:
nodes = ['A', 'B', 'C', 'D', 'E']
G.add_nodes_from(nodes)
# Add edges or links between the nodes:
edges = [('A','B'), ('B','C'), ('B', 'D'), ('D', 'E')]
G.add_edges_from(edges)
return G
G = createDiGraph()
# Get a layout for the nodes according to some algorithm.
pos = nx.layout.spring_layout(G, random_state=779)
node_size = 300
nodes = nx.draw_networkx_nodes(G, pos, node_size=node_size, node_color=(0,0,0.9),
edgecolors='black')
# nodes is a matplotlib.collections.PathCollection object
nodes.set_picker(5)
nx.draw_networkx_edges(G, pos, node_size=node_size, arrowstyle='->',
arrowsize=15, edge_color='black', width=1)
nx.draw_networkx_labels(G, pos, font_color='red', font_family='arial',
font_size=10)
def onpick(event):
if isinstance(event.artist, PathCollection):
all_nodes = event.artist
ind = event.ind[0] # event.ind is a single element array.
this_node_name = pos.keys()[ind]
print(this_node_name)
# Set the colours for all the nodes, highlighting the picked node with
# a different colour:
colors = [(0, 0, 0.9)] * len(pos)
colors[ind]=(0, 0.9, 0)
all_nodes.set_facecolors(colors)
# Update the plot to show the change:
fig.canvas.draw() # plt.draw() also works.
#fig.canvas.flush_events() # Not required? See
fig = plt.gcf()
# Bind our onpick() function to pick events:
fig.canvas.mpl_connect('pick_event', onpick)
# Hide the axes:
plt.gca().set_axis_off()
plt.show()
当您单击一个节点时,其表面颜色变为绿色,而其他节点设置为蓝色:
不幸的是,网络中的节点似乎由单个 matplotlib.artist.Artist
对象表示,该对象恰好是没有任何艺术家子节点的路径集合。这意味着您无法获取单个节点来更改其属性。相反,您被迫更新所有节点,只需确保所选节点的属性(在本例中为颜色)与其他节点不同。