Salome HOME
mergefrom branch BR_V511_PR tag mergeto_trunk_03feb09
[modules/yacs.git] / src / pyqt / gui / GraphViewer.py
1 #  Copyright (C) 2006-2008  CEA/DEN, EDF R&D
2 #
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.
7 #
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.
12 #
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
16 #
17 #  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19 import sys
20 from qt import *
21 from qtcanvas import *
22 import math
23
24 class DynamicTip( QToolTip ):
25   def __init__( self, parent ):
26     QToolTip.__init__( self, parent )
27
28   def maybeTip( self, pos ):
29     pos2=self.parentWidget().viewportToContents(pos)
30     point = self.parentWidget().inverseWorldMatrix().map(pos2)
31     ilist = self.parentWidget().canvas().collisions(point) #QCanvasItemList ilist
32     for each_item in ilist:
33       if hasattr(each_item,"tooltip"):
34         each_item.tooltip(self,pos)
35         return
36       elif hasattr(each_item,"getObj"):
37         each_item.getObj().tooltip(self,pos)
38         return
39
40 class GraphViewer(QCanvasView):
41   def __init__(self,c,parent,name,f):
42     QCanvasView.__init__(self,c,parent,name,f)
43     self.__moving=0
44     self.__connecting=0
45     self.__moving_start= 0
46     # for highlighting selections
47     self.selectPen=QPen(QColor(255,255,0),2,Qt.DashLine)
48     self.selectBrush=QBrush(Qt.red)
49     self.selectStyle = Qt.Dense5Pattern
50     self.selected=None
51     self.tooltip = DynamicTip( self )
52
53   def contentsMouseDoubleClickEvent(self,e): # QMouseEvent e
54     point = self.inverseWorldMatrix().map(e.pos())
55     ilist = self.canvas().collisions(point) #QCanvasItemList ilist
56     for each_item in ilist:
57       if each_item.rtti()==984376:
58         if not each_item.hit(point):
59           continue
60       if e.button()== Qt.LeftButton:
61         if hasattr(each_item,"handleDoubleClick"):
62           each_item.handleDoubleClick(point)
63           self.canvas().update()
64           return
65
66
67   def contentsMousePressEvent(self,e): # QMouseEvent e
68     p=e.globalPos()
69     point = self.inverseWorldMatrix().map(e.pos())
70     ilist = self.canvas().collisions(point) #QCanvasItemList ilist
71     for each_item in ilist:
72       if each_item.rtti()==984376:
73         if not each_item.hit(point):
74           continue
75       if e.button()== Qt.RightButton:
76         #Right button click
77         self.__moving=0
78         self.__connecting=0
79         if hasattr(each_item,"popup"):
80           menu=each_item.popup(self)
81           if menu:
82             menu.exec_loop( QCursor.pos() )
83             self.canvas().update()
84         elif hasattr(each_item,"getObj"):
85           menu=each_item.getObj().popup(self)
86           if menu:
87             menu.exec_loop( QCursor.pos() )
88             self.canvas().update()
89
90       elif e.button()== Qt.LeftButton:
91         #Left button click
92         if self.__connecting:
93           #We are linking objects
94           if hasattr(each_item,"getObj"):
95             #a connection is ending
96             self.__connecting.link(each_item.getObj().item)
97             #self.__connecting.link(each_item.getObj())
98             self.canvas().update()
99           self.__connecting=0
100         else:
101           #We are moving or selecting a composite object
102           each_item.selected()
103           self.__moving=each_item
104           self.__moving_start=point
105           self.canvas().update()
106       return
107     if e.button()== Qt.RightButton:
108       menu=self.popup()
109       if menu:
110         menu.exec_loop( QCursor.pos() )
111         self.canvas().update()
112     self.__moving=0
113     self.__connecting=0
114     QCanvasView.contentsMousePressEvent(self,e)
115
116   def selectItem(self,item):
117     #print "selectItem",item
118     if self.selected:
119       try:
120         self.deselectObj(self.selected)
121       except:
122         pass
123       self.selected=None
124     #need to find equivalent canvas item 
125     for citem in self.canvas().allItems():
126       if hasattr(citem,"item") and citem.item is item:
127         self.selected=citem
128         self.selectObj(self.selected)
129         break
130     self.canvas().update()
131
132   def selectObj(self,obj):
133     if obj:
134       obj._origPen = obj.pen()
135       obj._origBrush = obj.brush()
136       obj._origStyle = obj.brush().style()
137       obj.setPen(self.selectPen)
138       #obj.setBrush(self.selectBrush)
139
140   def deselectObj(self,obj):
141     if obj:
142       obj.setPen(obj._origPen)
143       #obj.setBrush(obj._origBrush)
144
145   def popup(self):
146     menu=QPopupMenu()
147     caption = QLabel( "<font color=darkblue><u><b>View Menu</b></u></font>", self )
148     caption.setAlignment( Qt.AlignCenter )
149     menu.insertItem( caption )
150     menu.insertItem("add Node", self.addNode)
151     return menu
152
153   #def layout(self,rankdir):
154   #  print rankdir
155
156   def updateCanvas(self):
157     #Par defaut, Qt n'efface pas le background. Seul repaintContents
158     #semble le faire. Utile apres un popup ou un resize avec scrollbars
159     #Peut-on l'utiliser partout ? Pb de performance ?
160     self.repaintContents(True)
161     #self.canvas().update()
162
163   def addNode(self):
164     print "addNode"    
165
166   def zoomIn(self):
167     m = self.worldMatrix()
168     m.scale( 2.0, 2.0 )
169     self.setWorldMatrix( m )
170
171   def zoomOut(self):
172     m = self.worldMatrix()
173     m.scale( 0.5, 0.5 )
174     self.setWorldMatrix( m )
175
176   def clear(self):
177     ilist = self.canvas().allItems()
178     for each_item in ilist:
179       if each_item:
180         each_item.setCanvas(None)
181         del each_item
182     self.canvas().update()
183
184   def connecting(self,obj):
185     """Method called by an item to notify canvasView a connection has begun"""
186     print "connecting",obj
187     self.__connecting=obj
188
189   def contentsMouseMoveEvent(self,e):
190     if  self.__moving :
191       point = self.inverseWorldMatrix().map(e.pos())
192       self.__moving.moveBy(point.x()-self.__moving_start.x(),point.y()-self.__moving_start.y())
193       self.__moving_start = point
194       self.canvas().update()
195     else:
196       #self.tooltip.maybeTip(point)
197       QCanvasView.contentsMouseMoveEvent(self,e)
198
199 class ImageItem(QCanvasRectangle):
200     def __init__(self,img,canvas):
201       QCanvasRectangle.__init__(self,canvas)
202
203       self.imageRTTI=984376
204       self.image=img
205       self.pixmap=QPixmap()
206       self.setSize(self.image.width(), self.image.height())
207       self.pixmap.convertFromImage(self.image, Qt.OrderedAlphaDither);
208
209     def rtti(self):
210       return self.imageRTTI
211
212     def hit(self,p):
213       ix = p.x()-self.x()
214       iy = p.y()-self.y()
215       if not self.image.valid( ix , iy ):
216         return False
217       self.pixel = self.image.pixel( ix, iy )
218       return  (qAlpha( self.pixel ) != 0)
219
220     def drawShape(self,p):
221       p.drawPixmap( self.x(), self.y(), self.pixmap )
222
223 class LinkItem(QCanvasLine):
224   def __init__(self,fromPort, toPort,canvas):
225     QCanvasLine.__init__(self,canvas)
226     self.setPen(QPen(Qt.black))
227     self.setBrush(QBrush(Qt.red))
228     fromPort.addOutLink(self)
229     toPort.addInLink(self)
230     self.setPoints(int(fromPort.x()),int(fromPort.y()), int(toPort.x()), int(toPort.y()))
231     self.setZ(min(fromPort.z(),toPort.z())-1)
232     self.setVisible(True)
233     self.arrow = QCanvasPolygon(self.canvas())
234     self.arrow.setBrush(QBrush(Qt.black))
235     self.setArrow()
236     self.arrow.show()
237
238   def setFromPoint(self,x,y):
239     self.setPoints(x,y,self.endPoint().x(),self.endPoint().y())
240     self.setArrow()
241
242   def setToPoint(self,x,y):
243     self.setPoints(self.startPoint().x(), self.startPoint().y(),x,y)
244     self.setArrow()
245
246   def moveBy(self,dx,dy):
247     pass
248
249   def setArrow(self):
250     x1,y1=self.startPoint().x(),self.startPoint().y()
251     x2,y2=self.endPoint().x(),self.endPoint().y()
252     d=math.hypot(x2-x1,y2-y1)
253     sina=(y2-y1)/d
254     cosa=(x2-x1)/d
255     x=(x1+x2)/2.
256     y=(y1+y2)/2.
257     pa=QPointArray(3)
258     pa.setPoint(0, QPoint(x+10*cosa,y+10*sina))
259     pa.setPoint(1, QPoint(x-5*sina,y+5*cosa))
260     pa.setPoint(2, QPoint(x+5*sina,y-5*cosa))
261     self.arrow.setPoints(pa)
262
263   def popup(self,canvasView):
264     menu=QPopupMenu()
265     caption = QLabel( "<font color=darkblue><u><b>Node Menu</b></u></font>",menu )
266     caption.setAlignment( Qt.AlignCenter )
267     menu.insertItem( caption )
268     menu.insertItem("Delete", self.delete)
269     return menu
270
271   def delete(self):
272     print "delete link"
273
274   def tooltip(self,view,pos):
275     r = QRect(pos.x(), pos.y(), pos.x()+10, pos.y()+10)
276     s = QString( "link: %d,%d" % (r.center().x(), r.center().y()) )
277     view.tip( r, s )
278
279 class PortItem(QCanvasEllipse):
280   def __init__(self,node,canvas):
281     QCanvasEllipse.__init__(self,6,6,canvas)
282     self.setPen(QPen(Qt.black))
283     self.setBrush(QBrush(Qt.red))
284     self.setZ(node.z()+1)
285     self.node=node
286
287   def moveBy(self,dx,dy):
288     self.node.moveBy(dx,dy)
289
290   def myMove(self,dx,dy):
291     QCanvasEllipse.moveBy(self,dx,dy)
292
293   def getObj(self):
294     return self
295
296   def popup(self,canvasView):
297     self.context=canvasView
298     menu=QPopupMenu()
299     caption = QLabel( "<font color=darkblue><u><b>Port Menu</b></u></font>",menu )
300     caption.setAlignment( Qt.AlignCenter )
301     menu.insertItem( caption )
302     menu.insertItem("Connect", self.connect)
303     return menu
304
305   def connect(self):
306     print "connect",self.context
307     self.context.connecting(self)
308
309   def link(self,obj):
310     print "link:",obj
311
312   def tooltip(self,view,pos):
313     r = QRect(self.x(), self.y(), self.width(), self.height())
314     s = QString( "port: %d,%d" % (r.center().x(), r.center().y()) )
315     view.tip( r, s )
316
317 class InPortItem(PortItem):
318   def __init__(self,node,canvas):
319     PortItem.__init__(self,node,canvas)
320     self.__inList=[]
321
322   def myMove(self,dx,dy):
323     PortItem.myMove(self,dx,dy)
324     for link in self.__inList:
325       link.setToPoint( int(self.x()), int(self.y()) )
326
327   def link(self,obj):
328     print "link:",obj
329     if isinstance(obj,OutPortItem):
330       #Connection possible
331       l=LinkItem(obj,self,self.canvas())
332
333   def addInLink(self,link):
334     self.__inList.append(link)
335
336 class OutPortItem(PortItem):
337   def __init__(self,node,canvas):
338     PortItem.__init__(self,node,canvas)
339     self.__outList=[]
340
341   def myMove(self,dx,dy):
342     PortItem.myMove(self,dx,dy)
343     for link in self.__outList:
344       link.setFromPoint( int(self.x()), int(self.y()) )
345
346   def link(self,obj):
347     print "link:",obj
348     if isinstance(obj,InPortItem):
349       #Connection possible
350       l=LinkItem(self,obj,self.canvas())
351
352   def addOutLink(self,link):
353     self.__outList.append(link)
354
355 class Cell(QCanvasRectangle):
356   def __init__(self,canvas):
357     QCanvasRectangle.__init__(self,canvas)
358     self.setSize(50,50)
359
360     self.inports=[]
361     self.outports=[]
362
363     p=InPortItem(self,canvas)
364     p.myMove(0,25)
365     self.inports.append(p)
366     p=OutPortItem(self,canvas)
367     p.myMove(50,25)
368     self.outports.append(p)
369
370   def moveBy(self,dx,dy):
371     QCanvasRectangle.moveBy(self,dx,dy)
372     for p in self.inports:
373       p.myMove(dx,dy)
374     for p in self.outports:
375       p.myMove(dx,dy)
376
377   def show(self):
378     QCanvasRectangle.show(self)
379     for p in self.inports:
380       p.show()
381     for p in self.outports:
382       p.show()
383
384   def getObj(self):
385     return self
386
387   def popup(self,canvasView):
388     menu=QPopupMenu()
389     caption = QLabel( "<font color=darkblue><u><b>Node Menu</b></u></font>",menu )
390     caption.setAlignment( Qt.AlignCenter )
391     menu.insertItem( caption )
392     menu.insertItem("Browse", self.browse)
393     return menu
394
395   def tooltip(self,view,pos):
396     r = QRect(self.x(), self.y(), self.width(), self.height())
397     s = QString( "node: %d,%d" % (r.center().x(), r.center().y()) )
398     view.tip( r, s )
399
400   def browse(self):
401     print "browse"
402
403   def selected(self):
404     print "selected"
405
406 if __name__=='__main__':
407   app=QApplication(sys.argv)
408   box=QMainWindow()
409   QToolBar(box,"toolbar")
410   canvas=QCanvas(800,600)
411   canvas.setAdvancePeriod(30)
412   m=GraphViewer(canvas,box,"example",0)
413   box.setCentralWidget(m)
414   c1=Cell(canvas)
415   c1.moveBy(150,150)
416   c1.show()
417   c2=Cell(canvas)
418   c2.moveBy(250,150)
419   c2.show()
420   c1.outports[0].link(c2.inports[0])
421
422   qApp.setMainWidget(box)
423   box.show()
424   app.exec_loop()
425
426