如何使用Cairo和Python在PDF中打印树表可视化?
问题如下: 我想使用Cairo和Python在PDF中可视化树表层次结构 我已将代码修改如下:如何使用Cairo和Python在PDF中打印树表可视化?,python,pdf,cairo,hierarchical,treetable,Python,Pdf,Cairo,Hierarchical,Treetable,问题如下: 我想使用Cairo和Python在PDF中可视化树表层次结构 我已将代码修改如下: 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
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")
它给了我这个:
但我想:
如果我能在循环中得到树叶子的层数,我就设置矩形高度=这个层数的叶子*textH,绘制表格 我添加了一些函数get_leaf_nodes来获取所有叶子并计算当前网格的高度 来自阿尔瓦罗·富恩特斯 网格高度现在很高。我不得不说堆栈溢出很好。总是给我我想要的 结果是:
虽然这段代码可以解决这个问题,但最好添加精化,并向可能不理解这段代码的人解释它是如何工作的。
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")