简体   繁体   English

如何使用Cairo和Python在PDF中打印树表可视化?

[英]How to print tree table visualization in PDF use Cairo and Python?

Following the question: 跟随问题:

Tree plotting in Python Python中的树图

The author's web site 作者的网站

I want to visualize the tree table (hierarchical structure) in PDF using Cairo and Python. 我想使用Cairo和Python可视化PDF中的树表(分层结构)。

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. 如果我可以在循环中获取树叶的级别编号,则将矩形高度设置为“(此级别的叶子)* textH”,绘制表格。

I have added some functions (get_leaf_nodes) to get all the leaf and computing the height of current grid. 我添加了一些函数(get_leaf_nodes)以获取所有叶子并计算当前网格的高度。

reference this question from Alvaro Fuentes 从Alvaro Fuentes 引用此问题

The grid height is right now.I have to say that Stack Overflow is great.Always give me what my want. 网格高度现在是正确的,我不得不说Stack Overflow很棒,总能给我我想要的东西。

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")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM