概述
以下基于python 生成自定义树状图,有基于treelib和不借助其他库的两种方法。
项目详情
def generate_custom_tree(nodes, parent_id=None, indent="", is_root=False):
"""
生成带换行的树形图
确保同级节点间保持一致的间隔,增强可读性
"""
tree_text = ""
children = [node for node in nodes if node["parent_id"] == parent_id]
child_count = len(children)
for i, child in enumerate(children):
# 节点前缀符号(最后一个子节点用└─,其余用├─)
prefix = "└─ " if i == child_count - 1 else "├─ "
tree_text += f"{indent}{prefix}{child['text']}\n"
# 递归处理子节点
child_indent = indent + (" " if i == child_count - 1 else "│ ")
subtree = generate_custom_tree(nodes, child["id"], child_indent)
tree_text += subtree
# 在每个节点(除了最后一个)处理完后添加换行
if i != child_count - 1:
# 根据当前层级计算合适的缩进空行
empty_line_indent = indent + (" " if "└─" in prefix else "│ ")
tree_text += f"{empty_line_indent}\n"
return tree_text
# --------------------------
# 1. 自定义节点数据(文字可任意改)
custom_nodes = [
{"id": 1, "parent_id": None, "text": "技能组"}, # 根节点(parent_id=None)
{"id": 2, "parent_id": 1, "text": "自然科学"}, # 一级节点
{"id": 3, "parent_id": 1, "text": "人文科学"}, # 一级节点
{"id": 4, "parent_id": 1, "text": "机体性能"},
{"id": 5, "parent_id": 2, "text": "软件"}, # 二级节点
{"id": 6, "parent_id": 2, "text": "硬件"}, # 二级节点
{"id": 7, "parent_id": 5, "text": "python"}, # 三级节点
]
# 2. 生成并打印树形图
tree_result = generate_custom_tree(custom_nodes)
print(tree_result)
from treelib import Tree
from treelib.exceptions import DuplicatedNodeIdError
def create_filesystem_tree():
# 创建树实例(根节点为“根目录”)
tree = Tree()
tree.create_node("Root Directory", "root") # (节点名称, 唯一ID)
# 一级目录:Documents/Pictures/Music/Programs
tree.create_node("Documents", "docs", parent="root")
tree.create_node("Pictures", "pics", parent="root")
tree.create_node("Music", "music", parent="root")
tree.create_node("Programs", "prog", parent="root")
# Documents 下的内容
tree.create_node("Notes.txt", "notes", parent="docs")
tree.create_node("Report.pdf", "report", parent="docs")
tree.create_node("Projects", "projects", parent="docs")
# Projects 下的内容(三级节点)
tree.create_node("Python", "python", parent="projects")
tree.create_node("Java", "java", parent="projects")
tree.create_node("C++", "cpp", parent="projects")
# Pictures 下的内容
tree.create_node("Family.jpg", "family", parent="pics")
tree.create_node("Nature", "nature", parent="pics")
tree.create_node("Vacation", "vacation", parent="pics")
# Music 下的内容
tree.create_node("Rock", "rock", parent="music")
tree.create_node("Jazz", "jazz", parent="music")
# Programs 下的内容
tree.create_node("Browser.exe", "browser", parent="prog")
tree.create_node("Editor.app", "editor", parent="prog")
return tree
def display_fixed_tree(tree):
"""修复后的自定义树显示:层级对齐、线条完整"""
if not tree:
return
print("Fixed File System Structure:")
print("=============================")
# 存储“父节点是否为最后一个子节点”的状态(用于绘制竖线)
# 例如:[False, True] 表示“一级父节点不是最后一个,二级父节点是最后一个”
parent_is_last = []
def print_node(node_id):
# 获取当前节点的深度和父节点
depth = tree.depth(node_id)
node = tree[node_id]
parent_node = tree.parent(node_id)
# 1. 处理缩进和竖线(关键:根据父节点状态判断是否显示竖线)
indent = ""
if depth > 0:
# 遍历所有父节点的“是否最后一个”状态
for is_last in parent_is_last[:-1]: # 排除当前父节点(最后一个元素)
# 非最后一个父节点:显示竖线(保持层级连贯);最后一个:显示空格
indent += "│ " if not is_last else " "
# 处理当前父节点的连接线(└── 或 ├──)
current_parent_is_last = parent_is_last[-1] if parent_is_last else False
indent += "└── " if current_parent_is_last else "├── "
# 2. 打印当前节点
print(f"{indent}{node.tag}")
# 3. 递归打印子节点(先获取所有子节点,保持顺序)
children = tree.children(node_id)
if children:
for i, child in enumerate(children):
# 记录当前子节点是否为“父节点的最后一个子节点”
is_last_child = (i == len(children) - 1)
parent_is_last.append(is_last_child)
# 递归打印子节点
print_node(child.identifier)
# 回溯:删除当前子节点的状态(避免影响其他分支)
parent_is_last.pop()
# 从根节点开始递归打印
print_node("root")
if __name__ == "__main__":
# 1. 创建文件树
fs_tree = create_filesystem_tree()
# 2. 显示库自带的正确结果(作为对比)
print("=== Library Default Display (Correct) ===")
fs_tree.show()
print("\n" + "="*60 + "\n")
# 3. 显示修复后的自定义结果(解决“线断”问题)
display_fixed_tree(fs_tree)
# 4. 保存修复后的结果到文件
with open("fixed_filesystem_tree.txt", "w", encoding="utf-8") as f:
# 重定义打印函数,将内容写入文件(而非控制台)
parent_is_last = []
def write_node(node_id):
depth = fs_tree.depth(node_id)
node = fs_tree[node_id]
parent_node = fs_tree.parent(node_id)
indent = ""
if depth > 0:
for is_last in parent_is_last[:-1]:
indent += "│ " if not is_last else " "
current_parent_is_last = parent_is_last[-1]
indent += "└── " if current_parent_is_last else "├── "
f.write(f"{indent}{node.tag}\n")
children = fs_tree.children(node_id)
for i, child in enumerate(children):
is_last_child = (i == len(children) - 1)
parent_is_last.append(is_last_child)
write_node(child.identifier)
parent_is_last.pop()
write_node("root")
print("\nFixed tree saved to 'fixed_filesystem_tree.txt'")