pythonnetworkximagemap

How to make a click-able graph by networkx?


I am trying to produce a clickable image of a graph in python. I directly called graphviz at first, then discovered networkx http://networkx.lanl.gov .

I'd like my program to obtain info about which node is displayed at the (x,y) coordinate where the user clicked the graph. I guess I could work with the pyplot window that opens and displays the graph, using the (x,y) coordinates at mouse-click, but I would need some kind of imagemap to know which node has been visualized at that coordinates!

Can you tell be if/how it can be done?


Solution

  • I solved it thanks to the good folks at http://groups.google.com/group/networkx-discuss ( http://groups.google.com/group/networkx-discuss/browse_thread/thread/aac227e1fb2a4719 ):

    The following (partial) code works in Tkinter, allows the creation of a matplotlib window (non blocking, by the way) containing a networkx graph, and executes the procedure visitNode() if you click on a given node.

    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()