1 # Copyright (C) 2006-2008 CEA/DEN, EDF R&D
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License.
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 # Lesser General Public License for more details.
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 from qtcanvas import *
24 from GraphViewer import GraphViewer
28 from pygraphviz import graphviz as gv
33 class MyCanvas(QCanvas):
34 def customEvent(self,event):
36 object.customEvent(event)
40 def __init__(self,item,parent):
44 #initial canvas size : 1000x1000
45 self.canvas=MyCanvas(1000,1000)
46 self.editor=GraphViewer(self.canvas,parent,"example",0)
48 root=self.node.getRootNode()
49 rootItem=Item.adapt(root)
50 CONNECTOR.Connect(rootItem,"selected",self.selectItem,())
51 CONNECTOR.Connect(self.item,"add",self.addItem,())
52 CONNECTOR.Connect(self.item.datalinks,"add",self.addLink,())
54 def createGraph(self):
55 #citems dict helps finding items in canvas from swig proxy
56 #To find an item node make : citems[node.ptr()]
59 #pitems dict helps finding items in canvas from swig proxy
60 #To find an item port make : pitems[port.ptr()]
64 lnode=self.node.edGetDirectDescendants()
66 c=CItems.Cell(n,self.canvas)
70 for k,n in citems.items():
72 pitems[p.port.ptr()]=p
74 pitems[p.port.ptr()]=p
76 for pout,pin in self.node.getSetOfInternalLinks():
77 if pout.getNode().getFather() != self.node and pin.getNode().getFather() != self.node:
79 po=pitems.get(pout.ptr())
80 pi=pitems.get(pin.ptr())
82 CItems.LinkItem(po,pi,self.canvas)
85 itemup=citems[n.ptr()]
86 for ndown in n.getOutNodes():
87 itemdown=citems[ndown.ptr()]
88 CItems.ControlLinkItem(itemup.outgate,itemdown.ingate,self.canvas)
92 def addLink(self,link):
93 print "graph.addLink",link
95 nodeS=self.citems[link.pout.getNode().ptr()]
96 nodeE=self.citems[link.pin.getNode().ptr()]
98 for p in nodeS.outports:
99 if p.port == link.pout:
102 for p in nodeE.inports:
103 if p.port == link.pin:
108 l=CItems.LinkItem(po,pi,self.canvas)
111 def addItem(self,item):
112 #print "graph.addItem",item
113 node=CItems.Cell(item.node,self.canvas)
114 self.citems[item.node.ptr()]=node
118 def selectItem(self,item):
119 #print "graph.selectItem",item
120 self.editor.selectItem(item)
122 def layout(self,rankdir):
123 """Compute graph layout with graphviz package"""
124 G=pygraphviz.AGraph(strict=False,directed=True)
125 G.graph_attr["rankdir"]=rankdir
126 G.graph_attr["dpi"]="72"
129 for k,n in self.citems.items():
130 #k is node address (YACS)
134 for pout,pin in self.node.getSetOfInternalLinks():
135 if pout.getNode().ptr() not in self.citems :
137 if pin.getNode().ptr() not in self.citems:
139 G.add_edge(pout.getNode().ptr(),pin.getNode().ptr())
141 for k,n in self.citems.items():
142 for ndown in n.node.getOutNodes():
143 G.add_edge(n.node.ptr(),ndown.ptr())
145 #By default graphviz uses 96.0 pixel per inch (dpi=96.0)
147 item=self.citems[int(n)]
148 h=item.height()/dpi #height in inch
149 w=item.width()/dpi #width in inch
150 n.attr['height']=str(h)
151 n.attr['width']=str(w)
152 n.attr['fixedsize']="true"
153 n.attr['shape']="box"
154 #n.attr['label']=item.node.getName()
156 G.layout(prog='dot') # use dot
157 #G.write("layout.dot")
158 #G.draw("layout.png")
160 graph_attr=dict(attrs(G))
161 bbox=graph_attr["bb"]
162 x1,y1,x2,y2=eval(bbox)
163 h=self.canvas.height()
164 w=self.canvas.width()
168 self.canvas.resize(w2,h2)
171 pos=n.attr['pos'] #position is given in points (72 points par inch, so 1 point = dpi/72=1.34)
175 item=self.citems[int(n)]
184 def clearLinks(self):
185 items=self.citems.values()
187 for port in node.outports:
188 if not hasattr(port,"links"):
190 for link in port.links():
194 def orthoLinks(self):
195 items=self.citems.values()
198 for port in node.outports:
199 if not hasattr(port,"links"):
201 for link in port.links():
202 #clear all intermediate points of the link
204 #if isinstance(link,CItems.ControlLinkItem):
205 # print port.port.getNode().getName() +"->"+link.toPort.port.getNode().getName()
207 # print port.port.getNode().getName() +":"+port.port.getName()+"->"+link.toPort.port.getNode().getName()+":"+link.toPort.port.getName()
208 #print (port.x(),port.y()),(link.toPort.x(),link.toPort.y())
209 x0,y0=port.x()+5,port.y()
210 while g.get((x0,y0)).blocked:
212 x1,y1=link.toPort.x()-5,link.toPort.y()
213 while g.get((x1,y1)).blocked:
215 path=g.findPath((x0,y0),(x1,y1))
218 if port.y() == link.toPort.y():
219 #near ports face to face
223 path=[(x,port.y()),(x,link.toPort.y())]
229 path=[(x1,port.y()),(x1,link.toPort.y())]
232 if port.y() == link.toPort.y():
233 #near ports face to face
236 #transform it into a vertical line
238 path=[(x,port.y()),(x,link.toPort.y())]
240 #adjust the first point to the same y as port
244 #remove first point and adjust second one
248 #adjust the last point to the same y as link.toPort
252 #remove last point and adjust new last one
255 path[-1]=x0,link.toPort.y()
258 #add intermediate points
261 link.splitLine(line,x,y)
264 def attrs(g,t=gv.AGRAPH):
267 ah=gv.agnxtattr(g.handle,t,ah)
268 value=gv.agxget(g.handle,ah)
269 yield gv.agattrname(ah),value
271 def h(x,y,destx,desty):
272 return abs(destx-x)+abs(desty-y)
274 def distance(node,new_node):
277 d= abs(x1-x)+abs(y1-y)
278 if node.parent != None:
279 x0,y0=node.parent.coord
280 if (x1-x)*(y0-y)-(y1-y)*(x0-x) != 0:
281 #corner add some cost to penalize
286 def __init__(self,coord,index):
297 def __init__(self,graph):
330 for w in xrange(len(xs)-1):
333 for h in xrange(len(ys)-1):
335 col.append(node((x,y),(w,h)))
336 self.cols.append(col)
343 l1,l2=bisect.bisect_left(ys,y-ymargin),bisect.bisect_left(ys,y+h+ymargin)
344 i1,i2=bisect.bisect_left(xs,x-xmargin),bisect.bisect_left(xs,x+w+xmargin)
345 for c in self.cols[i1:i2]:
349 #for col in self.cols:
350 # print [e.coord +(e.blocked,) for e in col]
356 n.total= n.path_cost=0
361 col= bisect.bisect_left(self.xs,x)-1
362 if col < 0 or col >= len(self.cols):
365 row=bisect.bisect_left(self.ys,y)-1
366 if row < 0 or row >= len(col):
370 def getPath(self,node):
378 #if points are aligned don't keep the middle point
382 if (x1-x)*(y0-y)-(y1-y)*(x0-x) == 0:
384 path.append(node.coord)
388 def neighbours(self,node):
391 steps=((0, +1), (+1, 0), (0, -1), (-1, 0 ))
395 if c < 0 or c >=len(self.cols):
398 if r <0 or r >= len(co):
406 def findPath(self,fromLoc,toLoc):
407 """Find shortest path from fromLoc to toLoc"""
410 fromNode=self.get(fromLoc)
411 self.open.append((fromNode.total,fromNode))
412 toNode=self.get(toLoc)
414 print "toNode is blocked"
416 destx,desty=toNode.coord
418 #if open is not void, take the best node (the first one)
419 t,node=self.open.pop(0)
423 return self.getPath(node)
425 for new_node in self.neighbours(node):
429 path_cost=node.path_cost+distance(node,new_node)
430 total=path_cost+h(x,y,destx,desty)
432 #the node is already in open
433 if total < new_node.total:
434 self.open.remove((new_node.total,new_node))
435 new_node.path_cost=path_cost
438 bisect.insort(self.open,(new_node.total,new_node))
440 #the node is not yet in open
441 new_node.path_cost=path_cost
444 bisect.insort(self.open,(new_node.total,new_node))