代码之家  ›  专栏  ›  技术社区  ›  andrew

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

  •  0
  • andrew  · 技术社区  · 7 年前

    问题如下:

    Tree plotting in Python

    The author's web site

    我想使用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, 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")
    

    这给了我:

    result

    但我想要的是:

    expected

    如果我能得到循环中树叶子的层数,我就设置矩形高度=(这个层的叶子)*textH“,绘制表格。

    1 回复  |  直到 7 年前
        1
  •  1
  •   andrew    7 年前

    我添加了一些函数(get\u leaf\u节点)来获取所有叶并计算当前网格的高度。

    reference this question 来自Alvaro Fuentes

    网格高度现在为。我不得不说,堆栈溢出非常好。总是给我我想要的。

    结果是:

    result

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