如何使用Cairo与Python实现树形表格的PDF可视化输出?
使用Python + Cairo绘制层级树形结构到PDF
基于你提供的代码框架,我补全了完整的实现逻辑,包括节点布局计算、Cairo绘图渲染和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.__bounds = [0, 0, 0, 0] # 存储节点位置:x, y, width, height self.__children = [] self.__parent = None @property def identifier(self): return self.__identifier @property def children(self): return self.__children @property def parent(self): return self.__parent @property def bounds(self): return self.__bounds @bounds.setter def bounds(self, value): self.__bounds = value def add_child(self, child): if child not in self.__children: child.__parent = self self.__children.append(child) def insert_child(self, index, child): if index < 0 or index > len(self.__children): raise IndexError("Index out of range") child.__parent = self self.__children.insert(index, child) def remove_child(self, child): if child in self.__children: child.__parent = None self.__children.remove(child) def calculate_tree_layout(root, spacing=50, node_width=150, node_height=50): """深度优先遍历计算每个节点的位置,确保层级布局合理""" def _calc_depth(node, depth): node.depth = depth total_height = node_height for child in node.children: _, child_h = _calc_depth(child, depth + 1) total_height += child_h + spacing return node_width, total_height # 先计算整个树的总尺寸 total_w, total_h = _calc_depth(root, 0) # 为每个节点分配坐标 def _assign_pos(node, x, y): node.bounds = [x, y, node_width, node_height] current_y = y + node_height + spacing for child in node.children: # 子节点向右偏移,形成层级缩进 child_x = x + spacing * 2 _assign_pos(child, child_x, current_y) current_y += child.bounds[3] + spacing _assign_pos(root, spacing, spacing) return total_w + spacing * 3, total_h + spacing * 2 def draw_tree_to_pdf(root, pdf_path, spacing=50, node_width=150, node_height=50): """将树形结构渲染到指定PDF文件""" # 先计算布局尺寸 surface_w, surface_h = calculate_tree_layout(root, spacing, node_width, node_height) # 创建Cairo PDF渲染表面 surface = cairo.PDFSurface(pdf_path, surface_w, surface_h) ctx = cairo.Context(surface) # 设置全局样式:字体、颜色 ctx.set_source_rgb(0.1, 0.1, 0.1) ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) ctx.set_font_size(14) def _draw_node(node): x, y, w, h = node.bounds # 绘制节点背景矩形 ctx.rectangle(x, y, w, h) ctx.set_source_rgb(0.9, 0.9, 0.9) ctx.fill_preserve() ctx.set_source_rgb(0.1, 0.1, 0.1) ctx.set_line_width(1) ctx.stroke() # 居中绘制节点名称 text_ext = ctx.text_extents(node.name) text_x = x + (w - text_ext[2]) / 2 text_y = y + (h + text_ext[3]) / 2 ctx.move_to(text_x, text_y) ctx.show_text(node.name) # 绘制到子节点的连接线 for child in node.children: cx, cy, _, _ = child.bounds # 父节点底部中点 parent_end_x = x + w/2 parent_end_y = y + h # 子节点顶部中点 child_start_x = cx + w/2 child_start_y = cy # 绘制折线连接线 ctx.move_to(parent_end_x, parent_end_y) ctx.line_to(parent_end_x, parent_end_y + spacing/2) ctx.line_to(child_start_x, child_start_y - spacing/2) ctx.line_to(child_start_x, child_start_y) ctx.set_line_width(1) ctx.stroke() # 递归绘制子节点 _draw_node(child) # 从根节点开始绘制整个树 _draw_node(root) surface.finish() # 测试示例:创建树形结构并生成PDF if __name__ == "__main__": # 构建示例树 root = Node("公司总部") dept1 = Node("技术部") dept1.add_child(Node("前端组")) dept1.add_child(Node("后端组")) dept2 = Node("市场部") dept2.add_child(Node("品牌推广组")) dept2.add_child(Node("销售组")) root.add_child(dept1) root.add_child(dept2) # 生成PDF draw_tree_to_pdf(root, "company_org_chart.pdf") print("组织结构图PDF已生成:company_org_chart.pdf")
功能说明
- Node类:完整封装了树形节点的属性和操作,包括父子关系管理、位置边界存储,满足层级结构的基本需求。
- 布局计算:通过深度优先遍历先统计整个树的高度,再为每个节点分配坐标,确保层级之间有清晰的缩进和间距。
- Cairo渲染:负责绘制节点的矩形背景、居中文字,以及父子节点间的连接线,生成的PDF结构清晰、样式统一。
- 自定义参数:你可以调整
spacing(间距)、node_width/node_height(节点尺寸)来适配不同的树形结构需求。
前置准备
确保已经安装了pycairo库:
pip install pycairo
内容的提问来源于stack exchange,提问作者andrew
相关产品推荐
相关产品推荐

