1 # Copyright (C) 2006-2016 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, or (at your option) any later version.
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
24 from qtcanvas import *
25 from .GraphViewer import GraphViewer
29 from pygraphviz import graphviz as gv
31 from . import CONNECTOR
34 class MyCanvas(QCanvas):
35 def customEvent(self,event):
37 object.customEvent(event)
41 def __init__(self,item,parent):
45 #initial canvas size : 1000x1000
46 self.canvas=MyCanvas(1000,1000)
47 self.editor=GraphViewer(self.canvas,parent,"example",0)
49 root=self.node.getRootNode()
50 rootItem=Item.adapt(root)
51 CONNECTOR.Connect(rootItem,"selected",self.selectItem,())
52 CONNECTOR.Connect(self.item,"add",self.addItem,())
53 CONNECTOR.Connect(self.item.datalinks,"add",self.addLink,())
55 def createGraph(self):
56 #citems dict helps finding items in canvas from swig proxy
57 #To find an item node make : citems[node.ptr()]
60 #pitems dict helps finding items in canvas from swig proxy
61 #To find an item port make : pitems[port.ptr()]
65 lnode=self.node.edGetDirectDescendants()
67 c=CItems.Cell(n,self.canvas)
71 for k,n in list(citems.items()):
73 pitems[p.port.ptr()]=p
75 pitems[p.port.ptr()]=p
77 for pout,pin in self.node.getSetOfInternalLinks():
78 if pout.getNode().getFather() != self.node and pin.getNode().getFather() != self.node:
80 po=pitems.get(pout.ptr())
81 pi=pitems.get(pin.ptr())
83 CItems.LinkItem(po,pi,self.canvas)
86 itemup=citems[n.ptr()]
87 for ndown in n.getOutNodes():
88 itemdown=citems[ndown.ptr()]
89 CItems.ControlLinkItem(itemup.outgate,itemdown.ingate,self.canvas)
93 def addLink(self,link):
94 print("graph.addLink",link)
96 nodeS=self.citems[link.pout.getNode().ptr()]
97 nodeE=self.citems[link.pin.getNode().ptr()]
99 for p in nodeS.outports:
100 if p.port == link.pout:
103 for p in nodeE.inports:
104 if p.port == link.pin:
109 l=CItems.LinkItem(po,pi,self.canvas)
112 def addItem(self,item):
113 #print "graph.addItem",item
114 node=CItems.Cell(item.node,self.canvas)
115 self.citems[item.node.ptr()]=node
119 def selectItem(self,item):
120 #print "graph.selectItem",item
121 self.editor.selectItem(item)
123 def layout(self,rankdir):
124 """Compute graph layout with graphviz package"""
125 G=pygraphviz.AGraph(strict=False,directed=True)
126 G.graph_attr["rankdir"]=rankdir
127 G.graph_attr["dpi"]="72"
130 for k,n in list(self.citems.items()):
131 #k is node address (YACS)
135 for pout,pin in self.node.getSetOfInternalLinks():
136 if pout.getNode().ptr() not in self.citems :
138 if pin.getNode().ptr() not in self.citems:
140 G.add_edge(pout.getNode().ptr(),pin.getNode().ptr())
142 for k,n in list(self.citems.items()):
143 for ndown in n.node.getOutNodes():
144 G.add_edge(n.node.ptr(),ndown.ptr())
146 #By default graphviz uses 96.0 pixel per inch (dpi=96.0)
148 item=self.citems[int(n)]
149 h=item.height()/dpi #height in inch
150 w=item.width()/dpi #width in inch
151 n.attr['height']=str(h)
152 n.attr['width']=str(w)
153 n.attr['fixedsize']="true"
154 n.attr['shape']="box"
155 #n.attr['label']=item.node.getName()
157 G.layout(prog='dot') # use dot
158 #G.write("layout.dot")
159 #G.draw("layout.png")
161 graph_attr=dict(attrs(G))
162 bbox=graph_attr["bb"]
163 x1,y1,x2,y2=eval(bbox)
164 h=self.canvas.height()
165 w=self.canvas.width()
169 self.canvas.resize(w2,h2)
172 pos=n.attr['pos'] #position is given in points (72 points par inch, so 1 point = dpi/72=1.34)
176 item=self.citems[int(n)]
185 def clearLinks(self):
186 items=list(self.citems.values())
188 for port in node.outports:
189 if not hasattr(port,"links"):
191 for link in port.links():
195 def orthoLinks(self):
196 items=list(self.citems.values())
199 for port in node.outports:
200 if not hasattr(port,"links"):
202 for link in port.links():
203 #clear all intermediate points of the link
205 #if isinstance(link,CItems.ControlLinkItem):
206 # print port.port.getNode().getName() +"->"+link.toPort.port.getNode().getName()
208 # print port.port.getNode().getName() +":"+port.port.getName()+"->"+link.toPort.port.getNode().getName()+":"+link.toPort.port.getName()
209 #print (port.x(),port.y()),(link.toPort.x(),link.toPort.y())
210 x0,y0=port.x()+5,port.y()
211 while g.get((x0,y0)).blocked:
213 x1,y1=link.toPort.x()-5,link.toPort.y()
214 while g.get((x1,y1)).blocked:
216 path=g.findPath((x0,y0),(x1,y1))
219 if port.y() == link.toPort.y():
220 #near ports face to face
224 path=[(x,port.y()),(x,link.toPort.y())]
230 path=[(x1,port.y()),(x1,link.toPort.y())]
233 if port.y() == link.toPort.y():
234 #near ports face to face
237 #transform it into a vertical line
239 path=[(x,port.y()),(x,link.toPort.y())]
241 #adjust the first point to the same y as port
245 #remove first point and adjust second one
249 #adjust the last point to the same y as link.toPort
253 #remove last point and adjust new last one
256 path[-1]=x0,link.toPort.y()
259 #add intermediate points
262 link.splitLine(line,x,y)
265 def attrs(g,t=gv.AGRAPH):
268 ah=gv.agnxtattr(g.handle,t,ah)
269 value=gv.agxget(g.handle,ah)
270 yield gv.agattrname(ah),value
272 def h(x,y,destx,desty):
273 return abs(destx-x)+abs(desty-y)
275 def distance(node,new_node):
278 d= abs(x1-x)+abs(y1-y)
279 if node.parent != None:
280 x0,y0=node.parent.coord
281 if (x1-x)*(y0-y)-(y1-y)*(x0-x) != 0:
282 #corner add some cost to penalize
287 def __init__(self,coord,index):
298 def __init__(self,graph):
331 for w in range(len(xs)-1):
334 for h in range(len(ys)-1):
336 col.append(node((x,y),(w,h)))
337 self.cols.append(col)
344 l1,l2=bisect.bisect_left(ys,y-ymargin),bisect.bisect_left(ys,y+h+ymargin)
345 i1,i2=bisect.bisect_left(xs,x-xmargin),bisect.bisect_left(xs,x+w+xmargin)
346 for c in self.cols[i1:i2]:
350 #for col in self.cols:
351 # print [e.coord +(e.blocked,) for e in col]
357 n.total= n.path_cost=0
362 col= bisect.bisect_left(self.xs,x)-1
363 if col < 0 or col >= len(self.cols):
366 row=bisect.bisect_left(self.ys,y)-1
367 if row < 0 or row >= len(col):
371 def getPath(self,node):
379 #if points are aligned don't keep the middle point
383 if (x1-x)*(y0-y)-(y1-y)*(x0-x) == 0:
385 path.append(node.coord)
389 def neighbours(self,node):
392 steps=((0, +1), (+1, 0), (0, -1), (-1, 0 ))
396 if c < 0 or c >=len(self.cols):
399 if r <0 or r >= len(co):
407 def findPath(self,fromLoc,toLoc):
408 """Find shortest path from fromLoc to toLoc"""
411 fromNode=self.get(fromLoc)
412 self.open.append((fromNode.total,fromNode))
413 toNode=self.get(toLoc)
415 print("toNode is blocked")
417 destx,desty=toNode.coord
419 #if open is not void, take the best node (the first one)
420 t,node=self.open.pop(0)
424 return self.getPath(node)
426 for new_node in self.neighbours(node):
430 path_cost=node.path_cost+distance(node,new_node)
431 total=path_cost+h(x,y,destx,desty)
433 #the node is already in open
434 if total < new_node.total:
435 self.open.remove((new_node.total,new_node))
436 new_node.path_cost=path_cost
439 bisect.insort(self.open,(new_node.total,new_node))
441 #the node is not yet in open
442 new_node.path_cost=path_cost
445 bisect.insort(self.open,(new_node.total,new_node))