graphcanvas.py :  » Development » Leo » Leo-4.7.1-final » leo » plugins » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » Leo 
Leo » Leo 4.7.1 final » leo » plugins » graphcanvas.py
"""Add a graph layout for nodes in a tab.  Requires backlink.py"""

__version__ = '0.1'
# 
# 0.1 - initial release - TNB

import leo.core.leoGlobals as g
import leo.core.leoPlugins as leoPlugins
from math import atan2,sin,cos

g.assertUi('qt')

from PyQt4 import QtCore,QtGui,uic
Qt = QtCore.Qt
def init ():

    if g.app.gui.guiName() != "qt":
        return False

    leoPlugins.registerHandler('after-create-leo-frame',onCreate)
    # can't use before-create-leo-frame because Qt dock's not ready
    g.plugin_signon(__name__)

    return True
def onCreate (tag, keys):

    c = keys.get('c')
    if not c: return

    graphcanvasController(c)
class graphcanvasUI(QtGui.QWidget):
    def __init__(self, owner=None):

        self.owner = owner

        # a = QtGui.QApplication([]) # argc, argv );

        QtGui.QWidget.__init__(self)
        uiPath = g.os_path_join(g.app.leoDir, 'plugins', 'GraphCanvas.ui')
        # uiPath = "GraphCanvas.ui"
        form_class, base_class = uic.loadUiType(uiPath)
        self.owner.c.frame.log.createTab('Graph', widget = self) 
        self.UI = form_class()
        self.UI.setupUi(self)

        self.canvas = QtGui.QGraphicsScene()

        self.canvasView = GraphicsView(self.owner, self.canvas)
        self.UI.canvasFrame.addWidget(self.canvasView)
        self.canvasView.setSceneRect(0,0,300,300)
        self.canvasView.setRenderHints(QtGui.QPainter.Antialiasing)
        u = self.UI
        o = self.owner

        self.connect(u.btnUpdate, QtCore.SIGNAL("clicked()"), o.update)
        self.connect(u.btnGoto, QtCore.SIGNAL("clicked()"), o.goto)

        self.connect(u.btnLoad, QtCore.SIGNAL("clicked()"), o.loadGraph)
        self.connect(u.btnLoadSibs, QtCore.SIGNAL("clicked()"),
            lambda: o.loadGraph('sibs'))
        self.connect(u.btnLoadRecur, QtCore.SIGNAL("clicked()"),
            lambda: o.loadGraph('recur'))

        self.connect(u.btnLoadLinked, QtCore.SIGNAL("clicked()"),
            lambda: o.loadLinked('linked'))
        self.connect(u.btnLoadAll, QtCore.SIGNAL("clicked()"),
            lambda: o.loadLinked('all'))

        self.connect(u.btnUnLoad, QtCore.SIGNAL("clicked()"), o.unLoad)
        self.connect(u.btnClear, QtCore.SIGNAL("clicked()"), o.clear)
class GraphicsView(QtGui.QGraphicsView):
    def __init__(self, glue, *args, **kargs):
        self.glue = glue
        QtGui.QGraphicsView.__init__(self, *args)
    def mouseDoubleClickEvent(self, event):
        QtGui.QGraphicsView.mouseDoubleClickEvent(self, event)
        nn = self.glue.newNode(pnt=self.mapToScene(event.pos()))
class nodeItem(QtGui.QGraphicsItemGroup):
    """Node on the canvas"""
    def __init__(self, glue, text, *args, **kargs):
        """:Parameters:
            - `glue`: glue object owning this

        pass glue object and let it key nodeItems to leo nodes
        """
        self.glue = glue
        QtGui.QGraphicsItemGroup.__init__(self, *args)
        self.text = QtGui.QGraphicsTextItem(text.replace(' ','\n'), *args)
        self.text.document().setDefaultTextOption(QtGui.QTextOption(Qt.AlignHCenter))
        self.setFlag(QtGui.QGraphicsItem.ItemIsMovable)
        self.text.setZValue(20)
        self.setZValue(20)
        self.bg = QtGui.QGraphicsRectItem(-2,+2,30,20)
        self.bg.setZValue(10)
        self.bg.setBrush(QtGui.QBrush(QtGui.QColor(200,240,200)))
        self.bg.setPen(QtGui.QPen(Qt.NoPen))
        self.addToGroup(self.text)
        self.addToGroup(self.bg)
    def mouseMoveEvent(self, event):
        QtGui.QGraphicsItemGroup.mouseMoveEvent(self, event)
        self.glue.newPos(self, event)
    def mouseReleaseEvent(self, event):
        QtGui.QGraphicsItemGroup.mouseReleaseEvent(self, event)
        self.glue.releaseNode(self, event)
class linkItem(QtGui.QGraphicsItemGroup):
    """Node on the canvas"""
    def __init__(self, glue, *args, **kargs):
        """:Parameters:
            - `glue`: glue object owning this

        pass glue object and let it key nodeItems to leo nodes
        """
        self.glue = glue
        QtGui.QGraphicsItemGroup.__init__(self)
        self.line = QtGui.QGraphicsLineItem(*args)
        self.line.setZValue(0)

        self.setZValue(0)
        pen = QtGui.QPen()
        pen.setWidth(2)
        self.line.setPen(pen)
        self.addToGroup(self.line)

        self.head = QtGui.QGraphicsPolygonItem()
        self.head.setBrush(QtGui.QBrush(QtGui.QColor(0,0,0)))
        self.head.setPen(QtGui.QPen(Qt.NoPen))
        self.addToGroup(self.head)
    def mousePressEvent(self, event):
        QtGui.QGraphicsItemGroup.mousePressEvent(self, event)
        self.glue.pressLink(self, event)
    def setLine(self, x0, y0, x1, y1):

        self.line.setLine(x0, y0, x1, y1)

        x,y = x1-(x1-x0)/3., y1-(y1-y0)/3.
        r = 12.
        a = atan2(y1-y0, x1-x0)
        w = 2.79252680
        pts = [
            QtCore.QPointF(x, y), 
            QtCore.QPointF(x+r*cos(a+w), y+r*sin(a+w)), 
            QtCore.QPointF(x+r*cos(a-w), y+r*sin(a-w)), 
            # QtCore.QPointF(x, y), 
        ]
        self.head.setPolygon(QtGui.QPolygonF(pts))
class graphcanvasController(object):
    """Display and edit links in leo"""

    def __init__ (self,c):

        self.c = c
        self.c.graphcanvasController = self
        self.ui = graphcanvasUI(self)

        leoPlugins.registerHandler('headkey2', lambda a,b: self.update())

        self.initIvars()

        # leoPlugins.registerHandler('open2', self.loadLinks)
        # already missed initial 'open2' because of after-create-leo-frame, so
        # self.loadLinksInt()
    def initIvars(self):
        """initialize, called by __init__ and clear"""

        self.node = {}
        self.nodeItem = {}
        self.link = {}
        self.linkItem = {}
        self.lastNodeItem = None
    def loadGraph(self, what='node', pnt=None):

        if what == 'sibs':
            collection = self.c.currentPosition().self_and_siblings()
        elif what == 'recur':
            collection = self.c.currentPosition().subtree()
        else:
            collection = [self.c.currentPosition()]

        for pos in collection:

            node = pos.v

            if node in self.nodeItem:
                continue

            txt = nodeItem(self, node.headString().replace(' ','\n'))

            self.node[txt] = node
            self.nodeItem[node] = txt

            if '_bklnk' not in node.u:
                node.u['_bklnk'] = {}

            x,y = 0,0
            if pnt:
                x,y = pnt.x(), pnt.y()
                node.u['_bklnk']['x'] = x
                node.u['_bklnk']['y'] = y
            elif 'x' in node.u['_bklnk']:
                x,y = node.u['_bklnk']['x'], node.u['_bklnk']['y']
            else:
                node.u['_bklnk']['x'] = x
                node.u['_bklnk']['y'] = y

            txt.setPos(x,y)
            self.ui.canvas.addItem(txt)

        self.update()
    def loadLinked(self, what='linked'):

        blc = getattr(self.c, 'backlinkController')
        if not blc:
            return

        while True:

            loaded = len(self.node)
            linked = set()

            for i in self.nodeItem:
                for j in blc.linksTo(i):
                    if j not in self.nodeItem:
                        linked.add(j)
                for j in blc.linksFrom(i):
                    if j not in self.nodeItem:
                        linked.add(j)

            for node in linked:

                txt = nodeItem(self, node.headString().replace(' ','\n'))

                self.node[txt] = node
                self.nodeItem[node] = txt

                if '_bklnk' in node.u and 'x' in node.u['_bklnk']:
                    txt.setPos(node.u['_bklnk']['x'], node.u['_bklnk']['y'])
                else:
                    node.u.setdefault('_bklnk',{})
                    # very important not to just node.u['_bklnk'] = {}
                    # as this would overwrite a backlinks.py dict with no x/y
                    node.u['_bklnk']['x'] = 0
                    node.u['_bklnk']['y'] = 0

                self.ui.canvas.addItem(txt)

            if not linked or what != 'all':
                # none added, or doing just one round
                break

        self.update()
    def addLinkItem(self, from_, to):
        if from_ not in self.nodeItem:
            return
        if to not in self.nodeItem:
            return
        key = (from_, to)
        if key in self.linkItem:
            return
        li = linkItem(self)
        self.setLinkItem(li, from_, to)

        self.linkItem[key] = li
        self.link[li] = key

        self.ui.canvas.addItem(li)
    def setLinkItem(self, li, from_, to):
        fromSize = self.nodeItem[from_].text.document().size()
        toSize = self.nodeItem[to].text.document().size()

        li.setLine(
            from_.u['_bklnk']['x'] + fromSize.width()/2, 
            from_.u['_bklnk']['y'] + fromSize.height()/2, 
            to.u['_bklnk']['x'] + toSize.width()/2, 
            to.u['_bklnk']['y'] + toSize.height()/2
            )
    def newPos(self, nodeItem, event):
        """nodeItem is telling us it has a new position"""
        node = self.node[nodeItem]
        node.u['_bklnk']['x'] = nodeItem.x()
        node.u['_bklnk']['y'] = nodeItem.y()

        blc = getattr(self.c, 'backlinkController')
        if blc:
            for link in blc.linksFrom(node):
                if (node, link) in self.linkItem:
                    self.setLinkItem(self.linkItem[(node, link)], node, link)

            for link in blc.linksTo(node):
                if (link, node) in self.linkItem:
                    self.setLinkItem(self.linkItem[(link, node)], link, node)

    def releaseNode(self, nodeItem, event):
        """nodeItem is telling us it has a new position"""

        if self.lastNodeItem == nodeItem:
            return


        #X node = self.node[nodeItem]
        #X node.u['_bklnk']['x'] = nodeItem.x()
        #X node.u['_bklnk']['y'] = nodeItem.y()

        if self.lastNodeItem:
            self.lastNodeItem.bg.setPen(QtGui.QPen(Qt.NoPen))
            # self.lastNodeItem.bg.setBrush(QtGui.QBrush(QtGui.QColor(200,240,200)))


        # nodeItem.bg.setBrush(QtGui.QBrush(QtGui.QColor(240,200,200)))
        nodeItem.bg.setPen(QtGui.QPen())

        oldItem = self.lastNodeItem
        self.lastNodeItem = nodeItem  # needed for self.goto()

        if self.ui.UI.chkTrack.isChecked():
            self.goto()

        blc = getattr(self.c, 'backlinkController')

        if not blc:
            return

        if event.modifiers() & Qt.ShiftModifier:
            links = blc.linksFrom(self.node[oldItem])
            if self.node[nodeItem] not in links:
                blc.vlink(self.node[oldItem], self.node[nodeItem])
                # blc will call our update(), so in retaliation...
                blc.updateTabInt()

    def newNode(self, pnt):
        nn = self.c.currentPosition().insertAfter()
        nn.setHeadString('node')
        self.c.selectPosition(nn)
        self.c.redraw_now()
        self.loadGraph(pnt=pnt)

    def pressLink(self, linkItem, event):
        """nodeItem is telling us it was clicked"""

        blc = getattr(self.c, 'backlinkController')

        if not blc:
            return

        if not (event.modifiers() & Qt.ControlModifier):
            return

        link = self.link[linkItem]

        v0, v1 = link

        # delete in both directions, only one will be needed, typically
        id0 = v0.gnx
        id1 = v1.gnx
        blc.deleteLink(v0, id1, 'S')
        blc.deleteLink(v1, id0, 'S')

        # blc will call our update(), so in retaliation...
        blc.updateTabInt()

        print('done')
    def unLoad(self):

        if not self.lastNodeItem:
            return

        node = self.node[self.lastNodeItem]

        self.ui.canvas.removeItem(self.lastNodeItem)

        culls = [i for i in self.linkItem if node in i]

        for i in culls:
            del self.link[self.linkItem[i]]
            self.ui.canvas.removeItem(self.linkItem[i])
            del self.linkItem[i]

        del self.nodeItem[node]
        del self.node[self.lastNodeItem]

        self.lastNodeItem = None
    def clear(self):

        for i in self.node:
            self.ui.canvas.removeItem(i)
        for i in self.link:
            self.ui.canvas.removeItem(i)

        self.initIvars()
    def update(self):
        """rescan name, links, extent"""
        for i in self.linkItem:
            self.ui.canvas.removeItem(self.linkItem[i])
        self.linkItem = {}

        blc = getattr(self.c, 'backlinkController')

        for i in self.nodeItem:
            self.nodeItem[i].text.setPlainText(i.headString().replace(' ','\n'))
            self.nodeItem[i].bg.setRect(-2, +2, 
                self.nodeItem[i].text.document().size().width()+4, 
                self.nodeItem[i].text.document().size().height()-2)

            if blc:
                for link in blc.linksFrom(i):
                    self.addLinkItem(i, link)
                for link in blc.linksTo(i):
                    self.addLinkItem(link, i)

        self.ui.canvasView.setSceneRect(self.ui.canvas.sceneRect().adjusted(-50,-50,50,50))
    def goto(self):
        """make outline select node"""
        v = self.node[self.lastNodeItem]
        p = self.c.vnode2position(v)
        if self.c.positionExists(p):
            self.c.selectPosition(p)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.