from PyQt4 import QtCore, QtGui, QtWebKit
[docs]class NodeColumn(QtGui.QGraphicsItem):
def __init__(self, vg, nodes, scene, left=None, right=None):
QtGui.QGraphicsItem.__init__(self, scene=scene)
self.setZValue( -100 ) # Get behind click events...
self._v_vg = vg
self._v_lines = []
self._v_nodes = nodes
# Remember if we srarted with something on our right or left
self._v_goleft = (left == None)
self._v_goright = (right == None)
self._v_left = left
self._v_right = right
offset = 0
for nid,nprops in nodes:
txt = QGraphNode(self, nid, nprops, scene=scene)
txt.setY( offset )
offset += txt.boundingRect().height()
[docs] def takeOverView(self):
self._removeLeft()
self._removeRight()
self.setX(0)
self.setY(0)
self._v_goleft = True
self._v_goright = True
def _removeLeft(self):
left = self._v_left
while left:
nextleft = left._v_left
left.removeColumn()
left = nextleft
def _removeRight(self):
right = self._v_right
while right:
nextright = right._v_right
right.removeColumn()
right = nextright
[docs] def removeColumn(self):
scene = self.scene()
for item in self._v_lines:
scene.removeItem( item )
scene.removeItem( self )
[docs] def boundingRect(self):
return self.childrenBoundingRect()
[docs] def paint(self, x, y, z): pass
[docs] def getRightBoundary(self):
rect = self.boundingRect()
return self.x() + rect.width()
[docs] def getLeftBoundary(self):
return self.x()
[docs] def getYMid(self):
return self.y() + (self.boundingRect().height() / 2)
[docs] def drawLinesTo(self, colnode):
'''
Draw lines to our nodes from the specified one
(used when we are on the right...)
'''
scene = self.scene()
colpos = colnode.scenePos()
colrect = colnode.boundingRect()
pen = None
ecolor = self._v_vg.getMeta('edgecolor')
if ecolor:
pen = QtGui.QPen( QtGui.QColor( ecolor) )
for item in self.childItems():
itpos = item.scenePos()
itrect = item.boundingRect()
x1 = colpos.x() + colrect.width()
y1 = colpos.y() + (colrect.height() / 2 )
x2 = itpos.x()
y2 = itpos.y() + ( itrect.height() / 2)
lineitem = scene.addLine(x1, y1, x2, y2, pen=pen)
self._v_lines.append( lineitem )
#lineitem.setParentItem(self)
[docs] def drawLinesFrom(self, colnode):
'''
Draw lines from our nodes to the specified one
(used when we are on the left...)
'''
scene = self.scene()
colpos = colnode.scenePos()
colrect = colnode.boundingRect()
pen = None
ecolor = self._v_vg.getMeta('edgecolor')
if ecolor:
pen = QtGui.QPen( QtGui.QColor( ecolor) )
for item in self.childItems():
itpos = item.scenePos()
itrect = item.boundingRect()
x1 = itpos.x() + itrect.width()
y1 = itpos.y() + ( itrect.height() / 2 )
x2 = colpos.x()
y2 = colpos.y() + ( colrect.height() / 2)
lineitem = scene.addLine(x1, y1, x2, y2, pen=pen)
self._v_lines.append( lineitem )
#lineitem.setParentItem(self)
[docs] def expandNode(self, colnode):
mymid = colnode.scenePos().y()
if self._v_goleft:
self._removeLeft()
nodes = [ (n1, self._v_vg.getNodeProps(n1)) for (eid, n1, n2, einfo) in self._v_vg.getRefsTo( colnode._v_nid ) ]
col = NodeColumn( self._v_vg, nodes, self.scene(), right=self )
hiswidth = col.boundingRect().width()
col.setX( self.getLeftBoundary() - hiswidth - 20 )
# Check if we want to scoot it down...
hismid = col.getYMid()
if hismid < mymid:
col.setY( col.y() + (mymid - hismid) )
col.drawLinesFrom( colnode )
self._v_left = col
if self._v_goright:
self._removeRight()
nodes = [ (n2, self._v_vg.getNodeProps(n2)) for (eid, n1, n2, einfo) in self._v_vg.getRefsFrom( colnode._v_nid ) ]
col = NodeColumn( self._v_vg, nodes, self.scene(), left=self )
col.setX( self.getRightBoundary() + 20 )
# Check if we want to scoot it down...
hismid = col.getYMid()
if hismid < mymid:
col.setY( col.y() + (mymid - hismid) )
col.drawLinesTo( colnode )
self._v_right = col
[docs]class QGraphNode(QtGui.QGraphicsSimpleTextItem):
def __init__(self, column, nid, nprops, scene=None):
QtGui.QGraphicsSimpleTextItem.__init__(self, nprops.get('repr', 'node:%d' % nid), scene=scene, parent=column)
self._v_nid = nid
self._v_nprops = nprops
color = nprops.get('color')
if color != None:
brush = QtGui.QBrush( QtGui.QColor( color ) )
self.setBrush( brush )
# TODO mouseDoubleClickEvent to take over center of view.
[docs] def mousePressEvent(self, event):
self.parentItem().expandNode( self )
# Our scene's parent is a QGraphTreeView...
self.scene().parent()._sig_NodeSelected.emit(self._v_nid, self._v_nprops)
[docs]class QGraphTreeView(QtGui.QGraphicsView):
_sig_NodeSelected = QtCore.pyqtSignal(int, dict)
_sig_NodeContextMenu = QtCore.pyqtSignal( object, int, dict ) # pos, nid, nprops
def __init__(self, vg, nodes, parent=None):
QtGui.QGraphicsView.__init__(self, parent=parent)
scene = QtGui.QGraphicsScene(parent=self)
self.setScene( scene )
self._v_nodecol = NodeColumn(vg, nodes, scene)
self._v_vg = vg
[docs] def loadNewGraph(self, vg, nodes):
self._v_nodecol._removeLeft()
self._v_nodecol._removeRight()
self._v_nodecol.removeColumn()
self._v_nodecol = NodeColumn(vg, nodes, self.scene() )
self._v_vg = vg
if __name__ == '__main__':
import sys
import visgraph.graphcore as vg_graphcore
app = QtGui.QApplication(sys.argv)
app.setFont( QtGui.QFont('Courier') )
initnodes = [ (i, {'repr':'node%d' % i}) for i in xrange( 30 ) ]
# Build up a fake graph
vg = vg_graphcore.Graph()
for nid,nprops in initnodes:
vg.addNode(nodeid=nid, repr='node %d' % nid)
for nid,nprops in initnodes:
for knid,knprops in initnodes:
if nid == knid:
continue
vg.addEdge(nid, knid)
win = QGraphTreeView( vg, initnodes, parent=None )
win.show()
app.exec_()