Following the question:
I want to visualize the tree table (hierarchical structure) in PDF using Cairo and Python.
I have modified the code as follows:
import uuid
import cairo
def sanitize_id(id):
return id.strip().replace(" ", "")
(_ADD, _DELETE, _INSERT) = range(3)
(_ROOT, _DEPTH, _WIDTH) = range(3)
class Node:
def __init__(self, name, identifier=None, expanded=True):
self.__identifier = (str(uuid.uuid1()) if identifier is None else
sanitize_id(str(identifier)))
self.name = name
self.expanded = expanded
self.__bpointer = None
self.__fpointer = []
@property
def identifier(self):
return self.__identifier
@property
def bpointer(self):
return self.__bpointer
@bpointer.setter
def bpointer(self, value):
if value is not None:
self.__bpointer = sanitize_id(value)
@property
def fpointer(self):
return self.__fpointer
def update_fpointer(self, identifier, mode=_ADD):
if mode is _ADD:
self.__fpointer.append(sanitize_id(identifier))
elif mode is _DELETE:
self.__fpointer.remove(sanitize_id(identifier))
elif mode is _INSERT:
self.__fpointer = [sanitize_id(identifier)]
class Tree(object):
def __init__(self, cr):
self._context = cr
self._colx = 50.0
self._coly = 50.0
self.textW = 128.0
self.textH = 20.0
self.nodes = []
def get_index(self, position):
for index, node in enumerate(self.nodes):
if node.identifier == position:
break
return index
def create_node(self, name, identifier=None, parent=None):
node = Node(name, identifier)
self.nodes.append(node)
self.__update_fpointer(parent, node.identifier, _ADD)
node.bpointer = parent
return node
def ShowText(self, x, y, st):
self._context.move_to(x, y)
self._context.show_text(st)
self._context.stroke()
def ShowRectText(self, x, y, w, h, st):
self.ShowText(x, y, st)
self._context.rectangle(x - 5, y - self.textH, w, h)
self._context.stroke()
def show(self, position, level=_ROOT):
queue = self[position].fpointer
h = self.textH*self.__len__()
if level == _ROOT:
s1 = "{0} [{1}]".format(self[position].name,
self[position].identifier)
self.ShowRectText(self._colx, self._coly, self.textW, h, s1)
self._coly = self._coly + self.textH
else:
s2 = "{0} [{1}]".format(self[position].name, self[position].identifier)
self._colx = self._colx + self.textW * level
self.ShowRectText(self._colx, self._coly, self.textW, h, s2)
self._coly = self._coly + self.textH
self._colx = self._colx - self.textW * level
self._context.stroke()
if self[position].expanded:
level += 1
for element in queue:
self.show(element, level) # recursive call
def expand_tree(self, position, mode=_DEPTH):
# Python generator. Loosly based on an algorithm from 'Essential LISP' by
# John R. Anderson, Albert T. Corbett, and Brian J. Reiser, page 239-241
yield position
queue = self[position].fpointer
while queue:
yield queue[0]
expansion = self[queue[0]].fpointer
if mode is _DEPTH:
queue = expansion + queue[1:] # depth-first
elif mode is _WIDTH:
queue = queue[1:] + expansion # width-first
def is_branch(self, position):
return self[position].fpointer
def __update_fpointer(self, position, identifier, mode):
if position is None:
return
else:
self[position].update_fpointer(identifier, mode)
def __update_bpointer(self, position, identifier):
self[position].bpointer = identifier
def __getitem__(self, key):
return self.nodes[self.get_index(key)]
def __setitem__(self, key, item):
self.nodes[self.get_index(key)] = item
def __len__(self):
return len(self.nodes)
def __contains__(self, identifier):
return [node.identifier for node in self.nodes
if node.identifier is identifier]
if __name__ == "__main__":
surface = cairo.PDFSurface("cairo_tree_table_show.pdf", 1000, 800)
context = cairo.Context(surface)
tree = Tree(context)
tree.create_node("Harry", "harry") # root node
tree.create_node("Jane", "jane", parent="harry")
tree.create_node("Bill", "bill", parent="harry")
tree.create_node("Joe", "joe", parent="jane")
tree.create_node("Diane", "diane", parent="jane")
tree.create_node("George", "george", parent="diane")
tree.create_node("Mary", "mary", parent="diane")
tree.create_node("Jill", "jill", parent="george")
tree.create_node("Carol", "carol", parent="jill")
tree.create_node("Grace", "grace", parent="bill")
tree.create_node("Mark", "mark", parent="jane")
tree.show("harry")
It gives me this:
but I want as:
If I can get level number of the tree leaf in the loop,I set the rectangle height = "(this level's leaf)*textH" ,draw the table.
I have added some functions (get_leaf_nodes) to get all the leaf and computing the height of current grid.
reference this question from Alvaro Fuentes
The grid height is right now.I have to say that Stack Overflow is great.Always give me what my want.
And result is :
import uuid
import cairo
def sanitize_id(id):
return id.strip().replace(" ", "")
(_ADD, _DELETE, _INSERT) = range(3)
(_ROOT, _DEPTH, _WIDTH) = range(3)
class Node:
def __init__(self, name, identifier=None, expanded=True):
self.__identifier = (str(uuid.uuid1()) if identifier is None else
sanitize_id(str(identifier)))
self.name = name
self.expanded = expanded
self.__bpointer = None
self.__fpointer = []
@property
def identifier(self):
return self.__identifier
@property
def bpointer(self):
return self.__bpointer
@bpointer.setter
def bpointer(self, value):
if value is not None:
self.__bpointer = sanitize_id(value)
@property
def fpointer(self):
return self.__fpointer
def update_fpointer(self, identifier, mode=_ADD):
if mode is _ADD:
self.__fpointer.append(sanitize_id(identifier))
elif mode is _DELETE:
self.__fpointer.remove(sanitize_id(identifier))
elif mode is _INSERT:
self.__fpointer = [sanitize_id(identifier)]
class Tree(object):
def __init__(self, cr):
self._context = cr
self._colx = 50.0
self._coly = 50.0
self.textW = 128.0
self.textH = 20.0
self.leafs = []
self.nodes = []
def get_leaf_nodes(self, position):
"""get all leafs"""
self.leafs = []
self._collect_leaf_nodes(position)
return self.leafs
def _collect_leaf_nodes(self, position):
queue = self[position].fpointer
if queue == []:
self.leafs.append(self[position])
else:
for n in queue:
self._collect_leaf_nodes(n)
def get_index(self, position):
for index, node in enumerate(self.nodes):
if node.identifier == position:
break
return index
def create_node(self, name, identifier=None, parent=None):
node = Node(name, identifier)
self.nodes.append(node)
self.__update_fpointer(parent, node.identifier, _ADD)
node.bpointer = parent
return node
def ShowText(self, x, y, st):
self._context.move_to(x, y)
self._context.show_text(st)
self._context.stroke()
def ShowRectText(self, x, y, w, h, st):
self.ShowText(x, y, st)
self._context.rectangle(x - 5, y - 0.8 * self.textH, w, h)
self._context.stroke()
def show(self, position, level=_ROOT):
queue = self[position].fpointer
# get all the children
h = self.textH * len(self.get_leaf_nodes(position))
if level == _ROOT:
s1 = "{0} [{1}]".format(self[position].name,
self[position].identifier)
self.ShowRectText(self._colx, self._coly, self.textW, h, s1)
else:
s2 = "{0} [{1}]".format(self[position].name, self[position].identifier)
self._colx = self._colx + self.textW * level
self.ShowRectText(self._colx, self._coly, self.textW, h, s2)
if queue==[]:
self._coly = self._coly + self.textH
self._colx = self._colx - self.textW * level
if self[position].expanded:
level += 1
for element in queue:
self.show(element, level) # recursive call
def expand_tree(self, position, mode=_DEPTH):
# Python generator. Loosly based on an algorithm from 'Essential LISP' by
# John R. Anderson, Albert T. Corbett, and Brian J. Reiser, page 239-241
# http://www.quesucede.com/page/show/id/python-3-tree-implementation
yield position
queue = self[position].fpointer
while queue:
yield queue[0]
expansion = self[queue[0]].fpointer
if mode is _DEPTH:
queue = expansion + queue[1:] # depth-first
elif mode is _WIDTH:
queue = queue[1:] + expansion # width-first
def is_branch(self, position):
return self[position].fpointer
def __update_fpointer(self, position, identifier, mode):
if position is None:
return
else:
self[position].update_fpointer(identifier, mode)
def __update_bpointer(self, position, identifier):
self[position].bpointer = identifier
def __getitem__(self, key):
return self.nodes[self.get_index(key)]
def __setitem__(self, key, item):
self.nodes[self.get_index(key)] = item
def __len__(self):
return len(self.nodes)
def __contains__(self, identifier):
return [node.identifier for node in self.nodes
if node.identifier is identifier]
if __name__ == "__main__":
surface = cairo.PDFSurface("cairo_tree_table_show.pdf", 1000, 800)
context = cairo.Context(surface)
tree = Tree(context)
tree.create_node("Harry", "harry") # root node
tree.create_node("Jane", "jane", parent="harry")
tree.create_node("Bill", "bill", parent="harry")
tree.create_node("Joe", "joe", parent="jane")
tree.create_node("Diane", "diane", parent="jane")
tree.create_node("George", "george", parent="diane")
tree.create_node("Mary", "mary", parent="diane")
tree.create_node("Jill", "jill", parent="george")
tree.create_node("Carol", "carol", parent="jill")
tree.create_node("Grace", "grace", parent="bill")
tree.create_node("Mark", "mark", parent="jane")
tree.show("harry")