绘制networkx.Graph:如何更改节点位置而不是重置每个节点?

问题描述

我正在一个项目中,需要创建nx.Graph()的预览,该预览允许更改使用鼠标拖动节点的节点的位置。如果在特定节点上单击鼠标,我的当前代码能够在每次鼠标移动后立即重绘整个图形。但是,这大大增加了等待时间。如何仅更新所需的艺术家,即单击的节点,其标签文本和相邻边缘,而不刷新每个plt.subplots()的艺术家?我至少可以参考所有需要搬迁的艺术家吗?

我从在networkx显示图形的标准方式开始:

import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import scipy.spatial

def refresh(G):
    plt.axis((-4,4,-1,3))
    nx.draw_networkx_labels(G,pos = nx.get_node_attributes(G,'pos'),bBox = dict(fc="lightgreen",ec="black",Boxstyle="square",lw=3))
    nx.draw_networkx_edges(G,width=1.0,alpha=0.5)
    plt.show()

nodes = np.array(['A','B','C','D','E','F','G'])
edges = np.array([['A','B'],['A','C'],['B','D'],'E'],['C','F'],'G']])
pos = np.array([[0,0],[-2,1],[2,[-3,2],[-1,[1,[3,2]])

G = nx.Graph()
# IG = InteractiveGraph(G) #>>>>> add this line in the next step
G.add_nodes_from(nodes)
G.add_edges_from(edges)
nx.set_node_attributes(G,dict(zip(G.nodes(),pos.astype(float))),'pos')

fig,ax = plt.subplots()
# fig.canvas.mpl_connect('button_press_event',lambda event: IG.on_press(event))
# fig.canvas.mpl_connect('motion_notify_event',lambda event: IG.on_motion(event))
# fig.canvas.mpl_connect('button_release_event',lambda event: IG.on_release(event))
refresh(G) # >>>>> replace it with IG.refresh() in the next step

https://medium.com/@ratheesh.archal/sidebar-menu-in-react-with-material-ui-d99747f90f1f

在下一步中,我更改了上一个脚本的5行(取消注释4行,并替换了1行),并使用了InteractiveGraph实例使其具有交互性:

class InteractiveGraph:
    def __init__(self,G,node_pressed=None,xydata=None):
        self.G = G
        self.node_pressed = node_pressed
        self.xydata = xydata

    def refresh(self,show=True):
        plt.clf()
        nx.draw_networkx_labels(self.G,pos = nx.get_node_attributes(self.G,lw=3))
        nx.draw_networkx_edges(self.G,alpha=0.5)
        plt.axis('off')
        plt.axis((-4,3))
        fig.patch.set_facecolor('white')
        if show:
            plt.show()

    def on_press(self,event):
        if event.inaxes is not None and len(self.G.nodes()) > 0:
            nodelist,coords = zip(*nx.get_node_attributes(self.G,'pos').items())
            kdtree = scipy.spatial.KDTree(coords)
            self.xydata = np.array([event.xdata,event.ydata])
            close_idx = kdtree.query_ball_point(self.xydata,np.sqrt(0.1))
            i = close_idx[0]
            self.node_pressed = nodelist[i]

    def on_motion(self,event):
        if event.inaxes is not None and self.node_pressed:
            new_xydata = np.array([event.xdata,event.ydata])
            self.xydata += new_xydata - self.xydata
            #print(d_xy,self.G.nodes[self.node_pressed])
            self.G.nodes[self.node_pressed]['pos'] = self.xydata
            self.refresh(show=False)
            event.canvas.draw()

    def on_release(self,event):
        self.node_pressed = None

enter image description here

相关来源

解决方法

要扩展我在上面的评论,请在netgraph中使用以下示例复制您的示例

import numpy as np
import matplotlib.pyplot as plt; plt.ion()
import networkx as nx
import netgraph

nodes = np.array(['A','B','C','D','E','F','G'])
edges = np.array([['A','B'],['A','C'],['B','D'],'E'],['C','F'],'G']])
pos = np.array([[0,0],[-2,1],[2,[-3,2],[-1,[1,[3,2]])

G = nx.Graph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)

I = netgraph.InteractiveGraph(G,node_positions=dict(zip(nodes,pos)),node_labels=dict(zip(nodes,nodes)),node_label_bbox=dict(fc="lightgreen",ec="black",boxstyle="square",lw=3),node_size=12,)

# move stuff with mouse

enter image description here


关于您编写​​的代码,如果您拥有所有艺术家的句柄,则不需要kd树。通常,matplotlib艺术家具有contains方法,这样,当您记录按钮按下事件时,您只需检查artist.contains(event)即可发现按钮按下是否发生在艺术家身上。当然,如果您使用networkx进行绘制,则无法以一种可查询的好形式获得句柄(ax.get_children()都不是),所以这是不可能的。