【问题标题】:How to make a click-able graph by networkx?如何通过networkx制作可点击的图表?
【发布时间】:2012-10-05 09:20:31
【问题描述】:

我正在尝试在 python 中生成可点击的图形图像。 一开始直接调用graphviz,后来发现networkxhttp://networkx.lanl.gov

我希望我的程序能够获取有关在用户单击图形的 (x,y) 坐标处显示哪个节点的信息。 我想我可以使用打开并显示图形的 pyplot 窗口,在鼠标单击时使用 (x,y) 坐标,但我需要某种图像映射来知道哪个 节点已在该坐标处可视化!

你能告诉我是否/如何做到吗?

【问题讨论】:

    标签: python networkx imagemap


    【解决方案1】:

    感谢http://groups.google.com/group/networkx-discuss 的好心人,我解决了这个问题 (http://groups.google.com/group/networkx-discuss/browse_thread/thread/aac227e1fb2a4719):

    以下(部分)代码在 Tkinter 中工作,允许创建包含 networkx 图的 matplotlib 窗口(顺便说一句,非阻塞),并在您单击给定节点时执行过程 visitNode()。

    import networkx as nx 
    import matplotlib.pyplot as plt 
    import pylab
    
    class AnnoteFinder:  # thanks to http://www.scipy.org/Cookbook/Matplotlib/Interactive_Plotting
        """
        callback for matplotlib to visit a node (display an annotation) when points are clicked on.  The
        point which is closest to the click and within xtol and ytol is identified.
        """
        def __init__(self, xdata, ydata, annotes, axis=None, xtol=None, ytol=None):
            self.data = zip(xdata, ydata, annotes)
            if xtol is None: xtol = ((max(xdata) - min(xdata))/float(len(xdata)))/2
            if ytol is None: ytol = ((max(ydata) - min(ydata))/float(len(ydata)))/2
            self.xtol = xtol
            self.ytol = ytol
            if axis is None: axis = pylab.gca()
            self.axis= axis
            self.drawnAnnotations = {}
            self.links = []
    
        def __call__(self, event):
            if event.inaxes:
                clickX = event.xdata
                clickY = event.ydata
                if self.axis is None or self.axis==event.inaxes:
                    annotes = []
                    for x,y,a in self.data:
                        if  clickX-self.xtol < x < clickX+self.xtol and  clickY-self.ytol < y < clickY+self.ytol :
                            dx,dy=x-clickX,y-clickY
                            annotes.append((dx*dx+dy*dy,x,y, a) )
                    if annotes:
                        annotes.sort() # to select the nearest node
                        distance, x, y, annote = annotes[0]
                        self.visitNode(annote)
    
        def visitNode(self, annote): # Visit the selected node
            # do something with the annote value
            print "visitNode", annote
    
    fig = plt.figure() ax = fig.add_subplot(111) ax.set_title('select nodes to navigate there')
    
    G=nx.MultiDiGraph()  # directed graph G = nx.wheel_graph(5)
    
    pos=nx.spring_layout(G) # the layout gives us the nodes position x,y,annotes=[],[],[] for key in pos:
        d=pos[key]
        annotes.append(key)
        x.append(d[0])
        y.append(d[1]) nx.draw(G,pos,font_size=8)
    
    af =  AnnoteFinder(x,y, annotes) fig.canvas.mpl_connect('button_press_event', af)
    
    plt.show()
    

    【讨论】:

    • 很抱歉,粘贴带有未定义名称和损坏标识的不完整/无效代码有什么意义?它会帮助你以外的人吗?
    • @minerals 我已提交修改以更正您提到的问题
    【解决方案2】:

    我在@minerals 发布的解决方案中发现了几个不完整的代码段。在这里,我发布了 python3 的工作代码段

    import networkx as nx
    import matplotlib.pyplot as plt
    import graphistry
    from pylab import *
    
    class AnnoteFinder:  # thanks to http://www.scipy.org/Cookbook/Matplotlib/Interactive_Plotting
        """
        callback for matplotlib to visit a node (display an annotation) when points are clicked on.  The
        point which is closest to the click and within xtol and ytol is identified.
        """
        def __init__(self, xdata, ydata, annotes, axis=None, xtol=None, ytol=None):
            self.data = list(zip(xdata, ydata, annotes))
            if xtol is None: xtol = ((max(xdata) - min(xdata))/float(len(xdata)))/2
            if ytol is None: ytol = ((max(ydata) - min(ydata))/float(len(ydata)))/2
            self.xtol = xtol
            self.ytol = ytol
            if axis is None: axis = gca()
            self.axis= axis
            self.drawnAnnotations = {}
            self.links = []
    
        def __call__(self, event):
            if event.inaxes:
                clickX = event.xdata
                clickY = event.ydata
                print(dir(event),event.key)
                if self.axis is None or self.axis==event.inaxes:
                    annotes = []
                    smallest_x_dist = float('inf')
                    smallest_y_dist = float('inf')
    
                    for x,y,a in self.data:
                        if abs(clickX-x)<=smallest_x_dist and abs(clickY-y)<=smallest_y_dist :
                            dx, dy = x - clickX, y - clickY
                            annotes.append((dx*dx+dy*dy,x,y, a) )
                            smallest_x_dist=abs(clickX-x)
                            smallest_y_dist=abs(clickY-y)
                            print(annotes,'annotate')
                        # if  clickX-self.xtol < x < clickX+self.xtol and  clickY-self.ytol < y < clickY+self.ytol :
                        #     dx,dy=x-clickX,y-clickY
                        #     annotes.append((dx*dx+dy*dy,x,y, a) )
                    print(annotes,clickX,clickY,self.xtol,self.ytol )
                    if annotes:
                        annotes.sort() # to select the nearest node
                        distance, x, y, annote = annotes[0]
                        self.drawAnnote(event.inaxes, x, y, annote)
    
        def drawAnnote(self, axis, x, y, annote):
            if (x, y) in self.drawnAnnotations:
                markers = self.drawnAnnotations[(x, y)]
                for m in markers:
                    m.set_visible(not m.get_visible())
                self.axis.figure.canvas.draw()
            else:
                t = axis.text(x, y, "%s" % (annote), )
                m = axis.scatter([x], [y], marker='d', c='r', zorder=100)
                self.drawnAnnotations[(x, y)] = (t, m)
                self.axis.figure.canvas.draw()
    
    
    df = pd.DataFrame("LOAD YOUR DATA")
    
    # Build your graph
    G = nx.from_pandas_edgelist(df, 'from', 'to')
    pos = nx.spring_layout(G,k=0.1, iterations=20)  # the layout gives us the nodes position x,y,annotes=[],[],[] for key in pos:
    x, y, annotes = [], [], []
    for key in pos:
        d = pos[key]
        annotes.append(key)
        x.append(d[0])
        y.append(d[1])
    
    fig = plt.figure(figsize=(10,10))
    ax = fig.add_subplot(111)
    ax.set_title('select nodes to navigate there')
    
    nx.draw(G, pos, font_size=6,node_color='#A0CBE2', edge_color='#BB0000', width=0.1,
                      node_size=2,with_labels=True)
    
    
    af = AnnoteFinder(x, y, annotes)
    connect('button_press_event', af)
    
    show()
    

    【讨论】:

      【解决方案3】:

      [更新亚历山德罗的回答]

      这是 alessandro 对 python 3 的更新答案。

      注意不推荐使用pylab。

      import networkx as nx
      import matplotlib.pyplot as plt
      import pylab
      
      
      class AnnoteFinder:  # thanks to http://www.scipy.org/Cookbook/Matplotlib/Interactive_Plotting
          """
          callback for matplotlib to visit a node (display an annotation) when points are clicked on.  The
          point which is closest to the click and within xtol and ytol is identified.
          """
      
          def __init__(self, xdata, ydata, annotes2, axis=None, xtol=None, ytol=None):
              self.data = zip(xdata, ydata, annotes2)
              if xtol is None:
                  xtol = ((max(xdata) - min(xdata)) / float(len(xdata))) / 2
              if ytol is None:
                  ytol = ((max(ydata) - min(ydata)) / float(len(ydata))) / 2
              self.xtol = xtol
              self.ytol = ytol
              if axis is None:
                  axis = pylab.gca()
              self.axis = axis
              self.drawnAnnotations = {}
              self.links = []
      
          def __call__(self, event):
              if event.inaxes:
                  clickX = event.xdata
                  clickY = event.ydata
                  if self.axis is None or self.axis == event.inaxes:
                      annotes2 = []
                      for x2, y2, a in self.data:
                          if clickX - self.xtol < x2 < clickX + self.xtol and clickY - self.ytol < y2 < clickY + self.ytol:
                              dx, dy = x2 - clickX, y2 - clickY
                              annotes2.append((dx * dx + dy * dy, x2, y2, a))
                      if annotes2:
                          annotes2.sort()  # to select the nearest node
                          distance, x2, y2, annote = annotes2[0]
                          self.visitnode(annote)
      
          def visitNode(self, annote):  # Visit the selected node
              # do something with the annote value
              print("visitNode", annote)
      
      
      fig = plt.figure()
      ax = fig.add_subplot(111)
      ax.set_title('select nodes to navigate there')
      
      G = nx.MultiDiGraph()  # directed graph G = nx.wheel_graph(5)
      
      pos = nx.spring_layout(G)  # the layout gives us the nodes position
      x, y, annotes = [], [], []
      for key in pos:
          d = pos[key]
          annotes.append(key)
          x.append(d[0])
          y.append(d[1])
          nx.draw(G, pos, font_size=8)
      
      af = AnnoteFinder(x, y, annotes)
      fig.canvas.mpl_connect('button_press_event', af)
      
      plt.show()
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-07-02
        • 2014-02-27
        • 1970-01-01
        • 2023-03-05
        • 2020-10-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多