7

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?

3 Answers 3

4

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() 
Sign up to request clarification or add additional context in comments.

2 Comments

I'm sorry but what is the point of pasting an incomplete/not working code with undefined names and broken identation? Will it help people besides yourself?
@minerals I've submitted an edit to correct the issues you mentioned
1

I found couple of incomplete code segments in the solution posted by @minerals. Here I am posting a working code segment for python3

import networkx as nx import matplotlib.pyplot as plt import pandas as pd 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() 

Comments

0

[Update of alessandro's answer]

Here is updated alessandro's answer for python 3.

Note that pylab usage is not recommended.

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

1 Comment

Why is pylab use not recommended?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.