Python API tutorial 5: graphs¶
Drawing custom graphs is a core use of the API. This tutorial builds a simple architecture-tree graph as a plugin. (Understand can already draw this — right-click a folder or architecture in the Architecture Browser → Graphical Views → Graph Architecture — but the code makes a good template to extend.)
Scripts become plugins¶
Until now you've run scripts from the command line. Understand turns any upython script into a plugin
by giving it the .upy extension and registering it. The quickest way is to drag the .upy file
onto the Understand GUI; see Install & run plugins for the plugin folders and
management. Once registered, a graph plugin appears in the Graphical Views list under the name you
give it.
Create a new file, arch_tree.upy.
Plugin hooks¶
A graph plugin defines a few well-known functions that Understand calls:
import understand
def name():
return "Tutorial: Architecture Tree"
def description():
return "Visually displays the hierarchical structure of a software architecture."
def test_architecture(arch):
return True
def init(graph, target):
graph.options().define("Fill", ["On", "Off"], "Off")
graph.legend().define("func", "roundedrect", "Function", "blue", "#FFFFFF")
name()anddescription()set how the plugin appears in the GUI.test_architecture(arch)returningTruetargets architectures (understand.Arch; there are matchingtest_*hooks for other target types).init(graph, target)runs before drawing — a good place to declare user-configurable options withgraph.options().define(...)and legend entries withgraph.legend().define(...).
The graph argument Understand passes to these hooks is an
understand.GraphContext (renamed from
understand.Graph in 8.0 — see Breaking API changes in 8.0).
Drawing logic¶
Understand uses Graphviz for layout; your plugin defines the nodes, edges, and subgraphs. First a helper that returns the graph node for an architecture, creating it once and reusing it thereafter:
def grabNode(graph, nodes, arch):
if arch in nodes:
node = nodes[arch]
else:
node = graph.node(arch.name())
node.sync(arch) # architectures must be synced, not passed at creation
nodes[arch] = node
return node
Then the draw function, which Understand calls with the graph and the selected target:
def draw(graph, target):
"""
Draw the graph. The second argument is the target the graph was created
for — here, always an architecture, since test_architecture() above is
the only test_* hook this plugin defines.
"""
graph.set("rankdir", "LR")
# Map arch -> node so each architecture appears once no matter how many edges
nodes = dict()
curLevel = [target]
while curLevel:
nextLevel = []
for arch in curLevel:
tail = grabNode(graph, nodes, arch)
for child in arch.children():
graph.edge(tail, grabNode(graph, nodes, child))
nextLevel.append(child)
curLevel = nextLevel
graph.set("rankdir", "LR")passes a Graphviz option — here, lay the graph out left-to-right.graph.node(label)creates a node;node.sync(arch)links it to the architecture so double-clicking it navigates in Understand.graph.edge(tail, head)connects two nodes.arch.children()returns an architecture's child architectures. The while loop walks the tree breadth-first: start at the selectedtarget, draw an edge to each child, then descend a level, until there are no more children.
More graph examples
The open-source plugin repository has many graph plugins to learn from, and the full Graph plugin reference is also under Help → Python API Documentation.
Next¶
You've built a graph plugin. Next you'll build an interactive text report the same way.
← Tutorial 4: lexers and lexemes · → Tutorial 6: interactive reports