Login | Register
My pages Projects Community openCollabNet

Discussions > cvs > CVS update: /leo/plugins/graphed.py

leo
Discussion topic

Back to topic list

CVS update: /leo/plugins/graphed.py

Author terry_n_brown
Full name Terry Brown
Date 2007-10-05 07:53:00 PDT
Message User: terry_n_brown
Date: 2007-10-05 07:53:00-0700
Log:
 graphed.py updated to 0.2, see in-file version history for changes
 

File Changes:

Directory: /leo/plugins/
========================

File [changed]: graphed.py
Url: http://leo.tigris.or​g/source/browse/leo/​plugins/graphed.py?r​1=1.1&r2=1.2
Delta lines: +309 -115
-----------------------
--- graphed.py 2007-10-04 07:28:45-0700 1.1
+++ graphed.py 2007-10-05 07:52:58-0700 1.2
@@ -5,9 +5,13 @@
 """
 graphed.py -- Edit graphs visually
 
-Based on the Gred graph editor from the Gato Graph Animation Toolbox
-at http://gato.sourceforge.net/
+Graph commands are in the Outline/Graph submenu.
+See http://leo.zwiki.org/GraphEd for documentation.
 
+Graph editor component based on the Gred graph editor from the
+Gato Graph Animation Toolbox at http://gato.sourceforge.net/
+
+Leo plugin by Terry Brown terry_n_brown at yahoo dot com
 """
 #@-node:ekr.20071004​090250.1:<< docstring >>
 #@nl
@@ -16,7 +20,7 @@
 #@@tabwidth -4
 #@@nowrap
 
-__version__ = "0.1"
+__version__ = "0.2"
 
 #@<< imports >>
 #@+node:ekr.20071004​090250.2:<< imports >>
@@ -33,17 +37,11 @@
 if gato_path not in sys.path:
     sys.path.append(gato_path)
 
-# GATO_path = "/home/tbrown/Deskto​p/Package/Gato"
-#
-# if GATO_path not in sys.path:
-# sys.path.append(GATO_path)
-
 try:
     from Gato import Gred, Embedder, Graph, GraphEditor, DataStructures
     Gato_ok = True
 except:
     Gato_ok = False
- g.es_print('ERROR: graphed: could not import Gato')
 #@-node:ekr.20071004​090250.2:<< imports >>
 #@nl
 #@<< version history >>
@@ -61,6 +59,12 @@
 # entire @thin node) to avoid conflict with cleo (!!)
 #
 # - Add leo/extensions/Gato to sys.path before importing from Gato.
+# 0.2 TNB:
+# - moved leo <-> graph stuff into separate class, much tidier not
+# mixing that with Gato stuff
+# - put x,y uAs on tnodes rather than vnodes because low level graph
+# class is a graph of tnodes (gnxs)
+# - implemented dot export functions
 #@-at
 #@-node:ekr.20071004​090250.3:<< version history >>
 #@nl
@@ -69,12 +73,8 @@
 #@+node:ekr.20071004​090250.9:init
 def init():
 
- if Tk is None:
- return False
-
     if not Gato_ok:
- g.es('graphed: Gato import failed',color='red')
- return False
+ g.es('graphed: Gato import failed, functions reduced',color='red')
 
     leoPlugins.registerH​andler('after-create​-leo-frame', onCreate)
     g.plugin_signon(__name__)
@@ -85,6 +85,204 @@
 def onCreate (tag,key):
     GraphEd(key['c'])
 #@-node:ekr.20071004​090250.10:onCreate
+#@+node:tbrown.2007​1004135224.1:class tGraph
+class tGraph:
+ """Minimalist graph-of-tnodes wrapper"""
+ #@ @+others
+ #@+node:tbrown.20071​004135224.2:__init__​
+ def __init__(self):
+ self._nodes = set()
+ self._edges = set()
+ self._gnxStr2tnode = {}
+ #@-node:tbrown.20071​004135224.2:__init__​
+ #@+node:tbrown.20071​004135224.3:nodes
+ def nodes(self):
+ """Return set of nodes"""
+ return self._nodes
+ #@-node:tbrown.20071​004135224.3:nodes
+ #@+node:tbrown.20071​004135224.4:edges
+ def edges(self):
+ """Return set of (node0, node1) tuples"""
+ return self._edges
+ #@-node:tbrown.20071​004135224.4:edges
+ #@+node:tbrown.20071​004135224.5:addNode
+ def addNode(self, n):
+ """Add n as a node"""
+ self._nodes.add(n)
+ #@-node:tbrown.20071​004135224.5:addNode
+ #@+node:tbrown.20071​004135224.6:addDirec​tedEdge
+ def addDirectedEdge(self, n0, n1):
+ """Add an edge from n0 and n1 as a node"""
+ self._edges.add((n0, n1))
+ #@-node:tbrown.20071​004135224.6:addDirec​tedEdge
+ #@+node:tbrown.20071​004141737:addGraphFr​omPosition
+ def addGraphFromPosition(self, p):
+ """
+ *Add* nodes and edges from the position and its descendants.
+
+ Need to add all the nodes before trying to resolve edges from @links
+ """
+
+ self._addNodesLinks(p)
+ self._addLinks(p)
+ #@-node:tbrown.20071​004141737:addGraphFr​omPosition
+ #@+node:tbrown.20071​004152905:createTree​FromGraph
+ def createTreeFromGraph(self, p):
+ """Build tree representing graph after p, assuming our nodes are tnodes"""
+
+ todo = set(self.nodes())
+
+ root = p.insertAfter()
+ root.setHeadString('new: graph top level')
+ root.expand()
+ pos = root.copy()
+
+ node2tnode = {} # for gnx lookups for making @links
+
+ inOut = {} # count in and out for each node
+ for n0, n1 in self.edges():
+ if n0 in inOut: # out edge
+ inOut[n0] = (inOut[n0][0], inOut[n0][1]+1)
+ else:
+ inOut[n0] = (0,1)
+ if n1 in inOut: # in edge
+ inOut[n1] = (inOut[n1][0]+1, inOut[n1][1])
+ else:
+ inOut[n1] = (1,0)
+
+ def nextStart(todo):
+ """find the node with the fewest in edges and most
+ out edges, writing these node first may give a more
+ human readable tree representation of the graph"""
+ maxOut = -1
+ minIn = 9999
+ maxIdx = None
+ for i in todo:
+ In = inOut[i][0]
+ out = inOut[i][1]
+ if In < minIn:
+ maxOut = out
+ minIn = In
+ maxIdx = i
+ if In == minIn and out > maxOut:
+ maxOut = out
+ minIn = In
+ maxIdx = i
+ return maxIdx
+
+ def makeTree(pos, node0):
+
+ nd = pos.insertAsLastChild()
+ nd.expand()
+ self._setIndex(nd)
+ node2tnode[node0] = str(nd.v.t.fileIndex)
+ nd.setHeadString(nod​e0.headString)
+ nd.setTnodeText(node​0.getBody())
+ if hasattr(node0, 'unknownAttributes'):
+ nd.v.t.unknownAttributes = dict(node0.unknownAttributes)
+
+ desc = [i[1] for i in self.edges() if i[0] == node0]
+ while desc:
+ node1 = nextStart(desc)
+ desc.remove(node1)
+
+ if node1 in todo:
+ todo.remove(node1)
+ makeTree(nd, node1)
+ else:
+ lnk = nd.insertAsLastChild()
+ lnk.setHeadString(
+ self._formatLink(nod​e2tnode[node1], node1.headString))
+
+ while todo:
+ next = nextStart(todo)
+ todo.remove(next)
+ makeTree(pos, next)
+
+ ans = pos
+
+ # if only one top level node, remove the holder node
+ if pos.numberOfChildren() == 1:
+ ch = pos.children_iter().next()
+ ch.linkAfter(pos)
+ ans = ch
+ pos.unlink()
+
+ return ans
+ #@-node:tbrown.20071​004152905:createTree​FromGraph
+ #@+node:tbrown.20071​004141737.1:_addNode​sLinks
+ def _addNodesLinks(self, p):
+ """Add nodes and simple descendent links from p"""
+
+ self.addNode(p.v.t)
+ self._setIndex(p)
+ self._gnxStr2tnode[s​tr(p.v.t.fileIndex)]​ = p.v.t
+
+ for nd0 in p.children_iter():
+ if nd0.headString().sta​rtswith('@link'): continue
+ self._addNodesLinks(nd0)
+ self.addDirectedEdge(p.v.t, nd0.v.t)
+ #@-node:tbrown.20071​004141737.1:_addNode​sLinks
+ #@+node:tbrown.20071​004141737.2:_addLink​s
+ def _addLinks(self, p):
+ """Collect the @links from p, now we know the nodes are in
+ self._gnxStr2tnode"""
+
+ for nd0 in p.children_iter():
+ if nd0.headString().sta​rtswith('@link'):
+ s = self._indexStrFromSt​r(nd0.headString())
+ try:
+ tnd = self._gnxStr2tnode[s]
+ self.addDirectedEdge(p.v.t, tnd)
+ except: # @link node went stale
+ g.es('Broken %s' % nd0.headString())
+ else:
+ self._addLinks(nd0)
+ #@-node:tbrown.20071​004141737.2:_addLink​s
+ #@+node:tbrown.20071​004141911:_setIndex
+ def _setIndex(self, p):
+ """fresh tnodes may not have .fileIndex, this adds it"""
+ try:
+ theId,time,n = p.v.t.fileIndex
+ except TypeError:
+ p.v.t.fileIndex = g.app.nodeIndices.getNewIndex()
+ #@-node:tbrown.20071​004141911:_setIndex
+ #@+node:tbrown.20071​004141931:_indexStrF​romStr
+ def _indexStrFromStr(self, s):
+ """isolate the '(...)' part of s"""
+ return s[s.find('(') : s.find(')')+1]
+ #@-node:tbrown.20071​004141931:_indexStrF​romStr
+ #@+node:tbrown.20071​004155803:_formatLin​k
+ def _formatLink(self, tid, hs):
+ """format @link headString,
+ strips '(' and ')' so _indexStrFromStr works"""
+ return '@link %s %s' % (hs.replace('(','[')​.replace(')',']'),st​r(tid))
+ #@-node:tbrown.20071​004155803:_formatLin​k
+ #@-others
+#@-node:tbrown.2007​1004135224.1:class tGraph
+#@+node:tbrown.2007​1004225829:class tGraphUtil
+class tGraphUtil(tGraph):
+ """Misc. utility functions on a tGraph"""
+
+ #@ @+others
+ #@+node:tbrown.20071​004225829.1:dotStrFr​omPosition
+ def dotStrFromPosition(self,p):
+ """return complete Graphviz dot format graph text"""
+ self.addGraphFromPosition(p)
+ node = {} # gnx to node number map
+ nodes = []
+ for n,i in enumerate(self.nodes()):
+ node[str(i.fileIndex)]=n
+ nodes.append('n%s [label="%s"]' % (n, i.headString))
+ edges = []
+ for f,t in self.edges():
+ edges.append('n%d -> n%d' % (node[str(f.fileIndex)],
+ node[str(t.fileIndex)]))
+
+ return 'digraph G {\n%s\n\n%s\n}' % ('\n'.join(nodes), '\n'.join(edges))
+ #@-node:tbrown.20071​004225829.1:dotStrFr​omPosition
+ #@-others
+#@-node:tbrown.2007​1004225829:class tGraphUtil
 #@+node:ekr.20071004​090250.11:class GraphEd
 class GraphEd:
 
@@ -97,13 +295,15 @@
         self.dictName = 'graphed' # for uA dictionary
 
         self.c = c
- table = (("Edit node as graph",None,self.editGraph),
+ table = []
+ if Gato_ok: table.append(("Edit node as graph",None,self.editGraph))
+ table += (
                  # BROKEN ("Edit whole tree as graph",None,self.edi​tWholeTree),
                  ("Copy link to clipboard",None,self.copyLink),
                  ("Follow link",None,self.followLink),
- ("Export to Graphviz dot format",None,self.undone), # FIXME
- ("Make Graphviz dot node",None,self.undone), # FIXME
- ("Layout using Graphviz dot",None,self.undone), # FIXME
+ ("Export to Graphviz dot format",None,self.dotFile),
+ ("Make Graphviz dot node",None,self.dotNode),
+ # CAN'T ("Layout using Graphviz dot",None,self.undone), # FIXME
                  )
         c.frame.menu.createN​ewMenu('Graph', 'Outline')
         c.frame.menu.createM​enuItemsFromTable('G​raph', table)
@@ -270,9 +470,12 @@
 
         self.p = p
 
- # make sure fileIndex is set on everything
- for p2 in p.self_and_subtree_iter():
- self.setIndex(p2)
+ tgraph = tGraph()
+ tgraph.addGraphFromPosition(p)
+
+ #X # make sure fileIndex is set on everything
+ #X for p2 in p.self_and_subtree_iter():
+ #X self.setIndex(p2)
 
         self.graph = Graph.Graph()
         # graph.simple = 0 # only blocks self loops?
@@ -280,8 +483,8 @@
 
         self.tnode2gnode = {}
         self.gnode2attribs = {}
- self.loadGraph(self.graph, p)
- self.loadGraphLinks(self.graph, p)
+ self.loadGraph(self.graph, tgraph)
+ # self.loadGraphLinks(self.graph, p)
 
         editor = Gred.SAGraphEditor(g.app.root)
         self.editor = editor
@@ -310,40 +513,23 @@
     #@-at
     #@-node:ekr.20071004​090250.27:editWholeT​ree
     #@+node:ekr.20071004​090250.28:loadGraph
- def loadGraph(self, graph, p):
+ def loadGraph(self, graph, tgraph):
 
+ for nd in tgraph.nodes():
         vid = graph.AddVertex()
- self.tnode2gnode[str​(p.v.t.fileIndex)] = vid
+ self.tnode2gnode[str​(nd.fileIndex)] = vid
         self.gnode2attribs[vid] = {}
- self.gnode2attribs[v​id]['bodyString'] = p.bodyString()
- graph.SetLabeling(vid, p.headString())
- x,y = self.getat(p.v,'x'), self.getat(p.v,'y')
+ self.gnode2attribs[v​id]['bodyString'] = nd.getBody()
+ graph.SetLabeling(vid, nd.headString)
+ x,y = self.getat(nd,'x'), self.getat(nd,'y')
         if x == None: x = 0
         if y == None: y = 0
         graph.SetEmbedding(vid, x, y)
 
- for nd0 in p.children_iter():
- if nd0.headString().sta​rtswith('@link'): continue
- cid = self.loadGraph(graph, nd0)
- graph.AddEdge(vid, cid)
- graph.SetEdgeWeight(0, vid, cid, 30)
- return vid
+ for nd0, nd1 in tgraph.edges():
+ graph.AddEdge(self.t​node2gnode[str(nd0.f​ileIndex)],
+ self.tnode2gnode[str​(nd1.fileIndex)])
     #@-node:ekr.20071004​090250.28:loadGraph
- #@+node:ekr.20071004​090250.29:loadGraphL​inks
- def loadGraphLinks(self, graph, p):
-
- for nd0 in p.children_iter():
- if nd0.headString().sta​rtswith('@link'):
- s = self.indexStrFromStr​(nd0.headString())
- vid = self.tnode2gnode[str​(p.v.t.fileIndex)]
- try:
- cid = self.tnode2gnode[s]
- graph.AddEdge(vid, cid)
- graph.SetEdgeWeight(0, vid, cid, 30)
- except: # @link node went stale
- g.es('Link to %s broke' % nd0.headString())
- self.loadGraphLinks(graph, nd0)
- #@-node:ekr.20071004​090250.29:loadGraphL​inks
     #@+node:ekr.20071004​090250.30:exiting
     def exiting(self):
         ans = g.app.gui.runAskYesN​oCancelDialog(
@@ -359,75 +545,53 @@
     #@+node:ekr.20071004​090250.31:saveGraph
     def saveGraph(self, p, graph):
 
+ class notAtnode(object):
+ """copy just enound of a tnodes signature to fool
+ tGraph.createTreeFromGraph - attribs will be set below"""
+
+ def getBody(self):
+ if hasattr(self, 'bodyString'):
+ return self.bodyString
+ else:
+ return ""
+
+ def label(i):
+ """change undefined (numeric) labels from ints to strs"""
+ return str(graph.GetLabeling(i))
+
         c = self.c
         c.beginUpdate()
         try:
- # FIXME should check p is ok and default to root if not
 
- todo = set(graph.Vertices())
+ tgraph = tGraph()
+ gnode2nottnode = {}
+ for node in graph.Vertices():
+ tn = notAtnode()
+ gnode2nottnode[node] = tn
+ tn.headString = label(node)
+ tn.unknownAttributes = {}
+ tn.unknownAttributes​[self.dictName] = {}
+ x = graph.GetEmbedding(node)
+ x,y = x.x,x.y
+ tn.unknownAttributes​[self.dictName]['x']​ = x
+ tn.unknownAttributes​[self.dictName]['y']​ = y
+
+ if node in self.gnode2attribs:
+ tn.bodyString = self.gnode2attribs[n​ode]['bodyString']
+ # FIXME copy uAs too
+ tgraph.addNode(tn)
+
+ for node0, node1 in graph.Edges():
+ tgraph.addDirectedEd​ge(gnode2nottnode[no​de0],gnode2nottnode[​node1])
 
             c.setHeadString(p, 'OLD: ' + p.headString())
             p.setDirty()
             c.selectPosition(p)
             c.contractNode()
- root = p.insertAfter()
- root.setHeadString('NEW: graph top level')
- root.expand()
- pos = root.copy()
-
- gnode2tnode = {}
-
- def label(i):
- """change undefined (numeric) labels from ints to strs"""
- return str(graph.GetLabeling(i))
-
- def nextStart():
- """find the node with the most outedges"""
- maxOut = -1
- maxIdx = None
- for i in todo:
- out = len(graph.OutNeighbors(i))
- if out > maxOut:
- maxOut = out
- maxIdx = i
- return maxIdx
-
- def makeTree(pos, node0):
-
- nd = pos.insertAsLastChild()
- nd.expand()
- self.setIndex(nd)
- gnode2tnode[node0] = str(nd.v.t.fileIndex)
- nd.setHeadString(label(node0))
- x = graph.GetEmbedding(node0)
- x,y = x.x,x.y
- self.setat(nd.v, 'x', x)
- self.setat(nd.v, 'y', y)
- if node0 in self.gnode2attribs:
- nd.setTnodeText(self​.gnode2attribs[node0​]['bodyString'])
- # FIXME copy uAs over too
-
- for node1 in graph.OutNeighbors(node0):
- if node1 in todo:
- todo.remove(node1)
- makeTree(nd, node1)
- else:
- lnk = nd.insertAsLastChild()
- lnk.setHeadString(
- self.formatLink(gnod​e2tnode[node1], label(node1)))
 
- while todo:
- next = nextStart()
- todo.remove(next)
- makeTree(pos, next)
+ newp = tgraph.createTreeFromGraph(p)
 
- if pos.numberOfChildren() == 1:
- ch = pos.children_iter().next()
- ch.linkAfter(pos)
- c.selectPosition(ch)
- pos.unlink()
- else:
- c.selectPosition(pos)
+ c.selectPosition(newp)
 
         finally:
             c.setChanged(True)
@@ -460,6 +624,36 @@
     def formatLink(self, tid, hs):
         return '@link %s %s' % (hs.replace('(','[')​.replace(')',']'),st​r(tid))
     #@-node:ekr.20071004​090250.34:formatLink​
+ #@+node:tbrown.20071​004225829.2:dotNode
+ def dotNode(self, event=None):
+ c = self.c
+ p = c.currentPosition()
+ t = p.headString()
+ tg = tGraphUtil()
+ dot = tg.dotStrFromPosition(p)
+ p = p.insertAfter()
+ c.setHeadString(p, 'DOT FORMAT: ' + t)
+ c.setBodyString(p, dot)
+ c.selectPosition(p)
+ #@-node:tbrown.20071​004225829.2:dotNode
+ #@+node:tbrown.20071​005092239:dotFile
+ def dotFile(self, event=None):
+ c = self.c
+ p = c.currentPosition()
+ t = p.headString()
+ tg = tGraphUtil()
+ dot = tg.dotStrFromPosition(p)
+ fn = g.app.gui.runSaveFileDialog(
+ '',
+ 'Save dot file to' ,
+ [('Dot', '*.dot'), ('All', '*.*')],
+ '.dot'
+ )
+ if not fn.lower().endswith('.dot'):
+ fn += '.dot'
+ file(fn, 'w').write(dot)
+ g.es('Wrote %s' % fn)
+ #@-node:tbrown.20071​005092239:dotFile
     #@+node:ekr.20071004​090250.35:undone
     def undone(self, event = None):
         g.app.gui.runAskOkDi​alog(self.c, 'Not implemented',

« Previous message in topic | 1 of 1 | Next message in topic »

Messages

Show all messages in topic

CVS update: /leo/plugins/graphed.py terry_n_brown Terry Brown 2007-10-05 07:53:00 PDT
Messages per page: