背景
应公司业务要求,开发一款支持多人在线编辑思维导图的产品,且要做到思维导图文件导入导出。前端组件 vue-testcase-minder-editor
,
组件作者:https://github.com/chenhengjie123/vue-testcase-minder-editor?tab=readme-ov-file
数据格式对比
#百度脑图数据格式
{'data': {'id': 'b7697411-883d-4494-933d-7da80c3fc63f', 'created': 1722849031301, 'text': '导出'
}, 'children': [
{'data': {'id': '2ecec96e-7364-4ea9-a2c1-dec85180a919', 'created': 1722849031301, 'text': '导出主题 1'
}, 'children': [
{'data': {'id': 'fb9a7734-19da-475f-9c9d-4ce152db3df8', 'created': 1722849031301, 'text': '哈哈哈哈哈'
}, 'children': []
},
{'data': {'id': '7b0ffb3e-fafd-42d2-b62e-20ff3e657ab0', 'created': 1722849031301, 'text': '高大上的v'
}, 'children': []
}
]
},
{'data': {'id': 'cbf24130-3b2d-4a7a-bbd1-28a85cc0f514', 'created': 1722849031301, 'text': '导出主题 2'
}, 'children': []
}
]
}
# xmind数据格式
[
{'title': '画布 1', 'topic': {'title': '导出', 'topics': [
{'title': '导出主题 1', 'topics': [
{'title': '哈哈哈哈哈', 'id': '6c4bbf9b-981c-44c7-9aa6-16d0c65a40f9'
},
{'title': '高大上的v', 'id': 'ea0e2537-9d51-4fdc-99df-13e44976a881'
}
], 'id': 'f7391556c0c84b1d9c259b582e'
},
{'title': '导出主题 2', 'id': '42b829374fcbb84ccd7af93f02'
}
], 'id': '17da8339e188988536c24ba1ce'
}, 'structure': 'org.xmind.ui.map.clockwise', 'id': 'bada207e86cb919b28692b4656'
}
]
导入--xmind转百度脑图数据格式
# 处理xmind导入数据格式
def transform_data(input_data):
def transform_node(node):
transformed_node = {
'data': {
'id': generate_unique_id(), # You need to implement generate_unique_id()
'created': generate_timestamp(), # You need to implement generate_timestamp()
'text': node['text']
},
'children': [transform_node(child) for child in node.get('children', [])]
}
return transformed_node
# Helper functions to generate unique IDs and timestamps (epoch time in milliseconds)
def generate_unique_id():
# You can use UUID or any other method to generate unique IDs
# Example implementation using UUID:
import uuid
return str(uuid.uuid4())
import time
def generate_timestamp():
# Generate current time in milliseconds
return int(round(time.time() * 1000))
# Start transformation from the root node
transformed_data = transform_node(input_data['data'])
return transformed_data
## 导入接口
def importParticulars(self, request, **kwargs):
# 获取当前脚本的文件路径
script_path = os.path.abspath(__file__)
# 获取当前脚本的目录(不包含文件名)
script_dir = os.path.dirname(script_path)
# 构建当前目录下的file文件路径
file_path = os.path.join(script_dir, 'file/')
# print(file_path)
# 清空file文件夹
folder_to_clear = file_path
clear_folder(folder_to_clear)
fs = FileSystemStorage(location=file_path)
particulars_id = request.data["particulars_id"]
file_data = request.data["file_data"]
# 接收前端传入数据存到file文件夹中 .xmind格式
uploaded_file_name = fs.save(file_data.name, file_data)
xmindparser.config = {
'showTopicId': True, # 是否展示主题ID
'hideEmptyValue': True # 是否隐藏空值
}
# 文件存的路径
filePath = os.path.join(file_path, uploaded_file_name)
# .xmind格式转成 .json格式文件
content = xmindparser.xmind_to_json(filePath)
# 读取json文件
with open(content, 'r', encoding='utf-8') as f:
data = json.load(f)
# 判断是否空文件
if len(data) == 0:
return APIRespones('1001', '空文件不可导入~', False)
res = data[0]['topic']
# 将res(字典格式)转成 JSON 字符串
original_json = '''{}'''.format(res)
# 使用正则表达式进行替换
converted_json = re.sub(r"'title':", "'created':'','text':", original_json)
# 将单引号转换为双引号,以符合 JSON 格式要求
converted_json = converted_json.replace("'", '"')
converted_json = re.sub(r'"([^"]+)"[\s\S]"([^"]+)"', r'\1\2', converted_json)
# print("converted_json",converted_json)
# converted_json = process_data(converted_json)
# 将转换后的 JSON 字符串加载为 Python 对象(字典)
converted_data = json.loads(converted_json)
# 再转一次(topics --> children)
original_json2 = '''{}'''.format(converted_data)
converted_json = re.sub(r"'topics':", "'children':", original_json2)
converted_json = converted_json.replace("'", '"')
converted_data2 = json.loads(converted_json)
finalData = {}
finalData['data'] = converted_data2
# 调用封装好的transform_data方法转数据格式,得到最终的前端组件可以渲染的数据格式
transformed_data = transform_data(finalData)
xmind_particulars.objects.filter(pk=particulars_id).update(file_data=transformed_data)
data = xmind_particulars.objects.filter(pk=particulars_id).values()
return APIRespones('1000', '文件导入成功~', True, data)
导入--百度脑图转xmind数据格式,并下载文件到本地
# 导出相关方法封装
"""
# 通过下方递归方法来改造以上这段代码
设置xmind文件主题以及子主题
for subtopic in map['children']:
print(9999,subtopic)
topic1 = root_topic1.addSubTopic()
topic1.setTitle(subtopic['data']['text'])
for subtopic2 in subtopic['children']:
print(888,subtopic2)
topic2 = topic1.addSubTopic()
topic2.setTitle(subtopic2['data']['text'])
for subtopic3 in subtopic2['children']:
print(777, subtopic3)
topic3 = topic2.addSubTopic()
topic3.setTitle(subtopic3['data']['text'])
for subtopic4 in subtopic3['children']:
print(777, subtopic4)
topic4 = topic3.addSubTopic()
topic4.setTitle(subtopic4['data']['text'])
"""
# 通过递归来改造以上这段代码
def add_subtopics(parent_topic, subtopics):
for subtopic in subtopics:
new_topic = parent_topic.addSubTopic()
new_topic.setTitle(subtopic['data']['text'])
# 如果有子主题,递归调用
if 'children' in subtopic:
add_subtopics(new_topic, subtopic['children'])
"""
踩坑:xmind python代码save后用xmind2020报错无法打开:原因xmind8格式无法用xmind2020打开,META-INF文件夹缺失,使用以下方法可解决
***************************************
xmind改成zip文件,
解压后 在目录内创建 META-INF文件夹
META-INF文件夹内创建 manifest.xml文件
文件内容:
'<?xml version="1.0" encoding="UTF-8" standalone="no"?><manifest xmlns="urn:xmind:xmap:xmlns:manifest:1.0" password-hint=""></manifest>'
重新打包后zip文件 然后后缀改成 xmind
"""
def repair(fname):
zip_file = zipfile.ZipFile(fname, 'a')
zip_file.writestr('META-INF/manifest.xml', '<?xml version="1.0" encoding="UTF-8" standalone="no"?><manifest xmlns="urn:xmind:xmap:xmlns:manifest:1.0" password-hint=""></manifest>')
zip_file.close()
# 导出xmind接口
def exportMindMap(self, request, **kwargs):
# 前端上传的文件数据
map = request.data["map"]
# 获取当前脚本的文件路径
script_path = os.path.abspath(__file__)
# 获取当前脚本的目录(不包含文件名)
script_dir = os.path.dirname(script_path)
# exportFile文件夹路径
exportFile_path = os.path.join(script_dir, 'exportFile')
# files = glob.glob(os.path.join(exportFile_path, '*.xmind'))
# # 删除后缀为 .xmind 的文件
# for file in files:
# os.remove(file)
clear_folder(exportFile_path) # 先清空exportFile文件
# 定义xmind文件名称
filename = 'output.xmind'
# 构建exportFile目录下的 'output.xmind' 文件路径
file_path = os.path.join(exportFile_path, filename)
# 创建一个新的xmind文件或加载已有xmind文件
workbook = xmind.load(file_path)
# 获取第一个画布
first_sheet = workbook.getPrimarySheet()
# 设置画布名称
first_sheet.setTitle(map['data']['text'])
# 获取画布中心主题,默认创建画布时会新建一个空白中心主题
root_topic1 = first_sheet.getRootTopic()
# 设置主题名称
root_topic1.setTitle(map['data']['text'])
# map['children'] 是最顶层的子主题
add_subtopics(root_topic1, map['children'])
# 保存并覆盖原始文件
xmind.save(workbook)
# 日志
develop.log.info('这是一个info级别的日志'+ file_path)
# 此方法解决生成的xmind文件本地使用xmind8之后的版本无法打开的问题
repair(file_path)
# 检查文件是否存在
if not os.path.exists(file_path):
return HttpResponse(status=404)
# 导出文件命名
xmindFileName = map['data']['text']
# 返回文件响应
response = FileResponse(open(file_path, 'rb'), content_type='application/xmind')
response['Content-Disposition'] = f'attachment; filename="{xmindFileName}"'
return response
python操作xmind文件
XMind-1.2.0.tar.gz
下载地址:
https://files.pythonhosted.org/packages/7c/8c/e13a82fa9b0394c0d58248196d7d51d7274407cdebc1df36b76034ab990d/XMind-1.2.0.tar.gz
创建及更新xmind文件
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import xmind
from xmind.core.const import TOPIC_DETACHED
from xmind.core.markerref import MarkerId
from xmind.core.topic import TopicElement
# 加载已有xmind文件,如果不存在,则新建
workbook = xmind.load('D:\\example\\example.xmind')
first_sheet = workbook.getPrimarySheet() # 获取第一个画布
first_sheet.setTitle('First Sheet') # 设置画布名称
root_topic1 = first_sheet.getRootTopic() # 获取画布中心主题,默认创建画布时会新建一个空白中心主题
root_topic1.setTitle('Example Topic') # 设置主题名称
sub_topic1 = root_topic1.addSubTopic() # 创建子主题,并设置名称
sub_topic1.setTitle("first sub topic")
sub_topic2 = root_topic1.addSubTopic()
sub_topic2.setTitle("second sub topic")
sub_topic3 = root_topic1.addSubTopic()
sub_topic3.setTitle("third sub topic")
# 除了新建子主题,还可以创建自由主题(注意:只有中心主题支持创建自由主题)
detached_topic1 = root_topic1.addSubTopic(topics_type=TOPIC_DETACHED)
detached_topic1.setTitle("detached topic")
detached_topic1.setPosition(0, 30)
# 创建一个子主题的子主题
sub_topic1_1 = sub_topic1.addSubTopic()
sub_topic1_1.setTitle("I'm a sub topic too")
second_sheet = workbook.createSheet() # 创建新画布
second_sheet.setTitle('Second Sheet')
root_topic2 = second_sheet.getRootTopic()
root_topic2.setTitle('Root Node')
# 使用其它方式创建子主题元素
topic1 = TopicElement(ownerWorkbook=workbook)
topic1.setTopicHyperlink(first_sheet.getID()) # 为画布创建一个来自第一个画布的主题链接
topic1.setTitle("redirection to the first sheet")
topic2 = TopicElement(ownerWorkbook=workbook)
topic2.setTitle("topic with an url hyperlink")
topic2.setURLHyperlink("https://www.cnblogs.com/shouke") # 为子主题元素设置URL超链接
topic3 = TopicElement(ownerWorkbook=workbook)
topic3.setTitle("third node")
topic3.setPlainNotes("notes for this topic") # 为子主题设置备注 (F4 in XMind)
topic3.setTitle("topic with \n notes")
topic4 = TopicElement(ownerWorkbook=workbook)
topic4.setFileHyperlink("d:\\example\demo.jpg") # 为子主题元素设置文件超链接
topic4.setTitle("topic with a file")
topic1_1 = TopicElement(ownerWorkbook=workbook)
topic1_1.setTitle("sub topic")
topic1_1.addLabel("a label") # 为子主题添加标签(official XMind only can a one label )
# 添加子主题到非中心主题
topic1.addSubTopic(topic1_1)
topic1_1_1 = TopicElement(ownerWorkbook=workbook)
topic1_1_1.setTitle("topic can add multiple markers")
# 为主题添加标记
topic1_1_1.addMarker(MarkerId.starBlue)
topic1_1_1.addMarker(MarkerId.flagGreen)
# 为子主题添加子主题
topic1_1.addSubTopic(topic1_1_1)
topic2_1 = TopicElement(ownerWorkbook=workbook)
topic2_1.setTitle("topic can add multiple comments")
# 为主题添加评论
topic2_1.addComment("I'm a comment!")
topic2_1.addComment(content="Hello comment!", author='devin')
topic2.addSubTopic(topic2_1)
# 添加子主题元素到中心主题
root_topic2.addSubTopic(topic1)
root_topic2.addSubTopic(topic2)
root_topic2.addSubTopic(topic3)
root_topic2.addSubTopic(topic4)
# 遍历子主题
topics = root_topic2.getSubTopics()
for index, topic in enumerate(topics):
topic.addMarker("priority-" + str(index + 1)) # 为主题添加标记(优先级图标)
# 为子主题1和子主题2创建关系
second_sheet.createRelationship(topic1.getID(), topic2.getID(), "relationship test")
# xmind.save(workbook) # 保存并覆盖原始文件
# 仅保存content.xml
# xmind.save(workbook=workbook, path="d:\\example\\other.xmind", only_content=True) # 不改动原始文件,另存为其它xmind文件
# 仅保存content.xml、comments.xml、styles.xml
# xmind.save(workbook=workbook, path="d:\\example\\other.xmind", except_revisions=True) # 不改动原始文件,另存为其它xmind文件
# 保存所有东西,Revisions除外,以节省空间(推荐)
# xmind.save(workbook=workbook, path="d:\\example\\other.xmind", except_revisions=True) # 不改动原始文件,另存为其它xmind文件
# 保存所有内容,并且另存为其它xmind文件(推荐)
xmind.save(workbook=workbook, path='d:\\example\\other.xmind') # 不改动原始文件,另存为其它xmind文件,等同 xmind.save(workbook, 'd:\\example\\exam.xmind')
运行结果
解析xmind文件
解析xmind文件
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json
import xmind
import pipes
def dict_to_prettify_json(data):
print(json.dumps(data, indent=4, separators=(',', ': ')))
def custom_parse_xmind(workbook):
elements = {}
def _echo(tag, element, indent=0):
title = element.getTitle()
elements[element.getID()] = title
print('\t' * indent, tag, ':', pipes.quote(title))
def dump_sheet(sheet):
root_topic = sheet.getRootTopic()
_echo('RootTopic', root_topic, 1)
for topic in root_topic.getSubTopics() or []:
_echo('AttachedSubTopic', topic, 2)
for topic in root_topic.getSubTopics(xmind.core.const.TOPIC_DETACHED) or []:
_echo('DetachedSubtopic', topic, 2)
for rel in sheet.getRelationships():
id1, id2 = rel.getEnd1ID(), rel.getEnd2ID()
print('Relationship: [%s] --> [%s]' % (elements.get(id1), elements.get(id2)))
# 遍历画布
for sheet in workbook.getSheets():
_echo('Sheet', sheet)
dump_sheet(sheet)
# 加载已有xmind文件,如果不存在,则新建
workbook = xmind.load('D:\\example\\example.xmind')
print(workbook.getData()) # 获取整个xmind数据(字典的形式)
dict_to_prettify_json(workbook.getData())
# 获取某个画布的数据(字典的形式)
first_sheet = workbook.getPrimarySheet()
dict_to_prettify_json(first_sheet.getData())
# 获取某个主题数据(字典的形式)
root_topic = first_sheet.getRootTopic()
dict_to_prettify_json(root_topic.getData())
# 获取评论数据
commentsbook = workbook.commentsbook
print(commentsbook.getData())
# 自定义解析
custom_parse_xmind(workbook)
标签:topic1,脑图,topic,导入,xmind,workbook,path,data
From: https://www.cnblogs.com/chenxdnote/p/18428142