自动化工具 (开源) XMind 思维导图创建、解析、更新的一站式解决方案!

Devin · 2019年01月02日 · 最后由 等待ing 回复于 2019年11月05日 · 5920 次阅读

mind_mapping

XMind 是基于 Python 实现,提供了对 XMind 思维导图进行创建、解析、更新的一站式解决方案!

一、安装方式

pip3 install XMind  

or

pip3 install xmind

二、版本升级

pip3 install -U XMind

三、使用方式

1、创建 XMind 文件

def gen_my_xmind_file():  
    # 1、如果指定的XMind文件存在,则加载,否则创建一个新的
    workbook = xmind.load("my.xmind")

    # 2、获取第一个画布(Sheet),默认新建一个XMind文件时,自动创建一个空白的画布
    sheet1 = workbook.getPrimarySheet()
    # 对第一个画布进行设计完善,具体参照下一个函数
    design_sheet1(sheet1)

    # 3、创建第二个画布
    gen_sheet2(workbook, sheet1)

    # 4、保存(如果指定path参数,另存为该文件名)
    xmind.save(workbook, path='test.xmind')

first sheet

def design_sheet1(sheet1):
    # ***** 第一个画布 *****
    sheet1.setTitle("first sheet")  # 设置画布名称

    # 获取画布的中心主题,默认创建画布时会新建一个空白中心主题
    root_topic1 = sheet1.getRootTopic()
    root_topic1.setTitle("root node")  # 设置主题名称

    # 创建一个子主题,并设置其名称
    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")

    sub_topic4 = root_topic1.addSubTopic()
    sub_topic4.setTitle("fourth 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

def gen_sheet2(workbook, sheet1):
    # ***** 设计第二个画布 *****
    sheet2 = workbook.createSheet()
    sheet2.setTitle("second sheet")

    # 获取画布的中心主题
    root_topic2 = sheet2.getRootTopic()
    root_topic2.setTitle("root node")

    # 使用另外一种方法创建子主题
    topic1 = TopicElement(ownerWorkbook=workbook)
    # 给子主题添加一个主题间超链接,通过指定目标主题ID即可,这里链接到第一个画布
    topic1.setTopicHyperlink(sheet1.getID())
    topic1.setTitle("redirection to the first sheet")

    topic2 = TopicElement(ownerWorkbook=workbook)
    topic2.setTitle("topic with an url hyperlink")
    # 给子主题添加一个URL超链接
    topic2.setURLHyperlink("https://github.com/zhuifengshen/xmind")

    topic3 = TopicElement(ownerWorkbook=workbook)
    topic3.setTitle("third node")
    # 给子主题添加一个备注(快捷键F4)
    topic3.setPlainNotes("notes for this topic")
    topic3.setTitle("topic with \n notes")

    topic4 = TopicElement(ownerWorkbook=workbook)
    # 给子主题添加一个文件超链接
    topic4.setFileHyperlink("logo.png")
    topic4.setTitle("topic with a file")

    topic1_1 = TopicElement(ownerWorkbook=workbook)
    topic1_1.setTitle("sub topic")
    # 给子主题添加一个标签(目前XMind软件仅支持添加一个,快捷键)
    topic1_1.addLabel("a label")

    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)

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

    # 将创建好的子主题添加到其父主题下
    root_topic2.addSubTopic(topic1)
    root_topic2.addSubTopic(topic2)
    root_topic2.addSubTopic(topic3)
    root_topic2.addSubTopic(topic4)
    topic1.addSubTopic(topic1_1)
    topic2.addSubTopic(topic2_1)
    topic1_1.addSubTopic(topic1_1_1)

    # 给中心主题下的每个子主题添加一个优先级图标
    topics = root_topic2.getSubTopics()
    for index, topic in enumerate(topics):
        topic.addMarker("priority-" + str(index + 1))

    # 添加一个主题与主题之间的联系
    sheet2.createRelationship(topic1.getID(), topic2.getID(), "relationship test") 

具体代码参考:create_xmind.py

2、解析 XMind 文件

(1) 将 XMind 文件转换为 Dict 数据 / JSON 数据
import xmind
workbook = xmind.load('demo.xmind')
print(workbook.getData())
print(workbook.to_prettify_json())


Output:

[                                                                # 画布列表
    {                                                            # 第1个画布数据
        "id": "2cc3b068922063a81a20029655",                      # 画布ID
        "title": "first sheet",                                  # 画布名称
        "topic": {                                               # 中心主题
            "id": "2cc3b06892206f95288e487b6c",                  # 主题ID
            "link": null,                                        # 超链接信息
            "title": "root node",                                # 主题名称
            "note": null,                                        # 备注信息
            "label": null,                                       # 便签信息
            "comment": null,                                     # 批注(评论)信息
            "markers": [],                                       # 图标列表
            "topics": [                                          # 子主题列表
                {
                    "id": "2cc3b06892206c816e1cb55ddc",          # 子主题ID
                    "link": null,
                    "title": "first sub topic",
                    "note": null,
                    "label": null,
                    "comment": null,
                    "markers": [],
                    "topics": [                                  # 子主题下的子主题列表
                        {
                            "id": "b0ed74214dbca939935b981906",
                            "link": null,
                            "title": "I'm a sub topic too",
                            "note": null,
                            "label": null,
                            "comment": null,
                            "markers": []
                        }
                    ]
                },
                {
                    "id": "b0ed74214dbca693b947ef03fa",
                    "link": null,
                    "title": "second sub topic",
                    "note": null,
                    "label": null,
                    "comment": null,
                    "markers": []
                },
                {
                    "id": "b0ed74214dbca1fe9ade911b94",
                    "link": null,
                    "title": "third sub topic",
                    "note": null,
                    "label": null,
                    "comment": null,
                    "markers": []
                },
                {
                    "id": "b0ed74214dbcac00c0eb368b53",
                    "link": null,
                    "title": "fourth sub topic",
                    "note": null,
                    "label": null,
                    "comment": null,
                    "markers": []
                }
            ]
        }
    },
    {
        "id": "b0ed74214dbcafdd0799f81ebf",
        "title": "second sheet",                                         # 第2个画布数据
        "topic": {
            "id": "b0ed74214dbcac7567f88365c2",
            "link": null,
            "title": "root node",
            "note": null,
            "label": null,
            "comment": null,
            "markers": [],
            "topics": [
                {
                    "id": "b0ed74214dbca8bfdc2b60df47",
                    "link": "xmind:#2cc3b068922063a81a20029655",
                    "title": "redirection to the first sheet",
                    "note": null,
                    "label": null,
                    "comment": null,
                    "markers": [
                        "priority-1"
                    ],
                    "topics": [
                        {
                            "id": "e613d79938591579e707a7a161",
                            "link": null,
                            "title": "sub topic",
                            "note": null,
                            "label": "a label",
                            "comment": null,
                            "markers": [],
                            "topics": [
                                {
                                    "id": "e613d799385912cca5eb579fb3",
                                    "link": null,
                                    "title": "topic can add multiple markers",
                                    "note": null,
                                    "label": null,
                                    "comment": null,
                                    "markers": [
                                        "star-blue",
                                        "flag-green"
                                    ]
                                }
                            ]
                        }
                    ]
                },
                {
                    "id": "e613d79938591ef98b64a768db",
                    "link": "https://xmind.net",
                    "title": "topic with an url hyperlink",
                    "note": null,
                    "label": null,
                    "comment": null,
                    "markers": [
                        "priority-2"
                    ],
                    "topics": [
                        {
                            "id": "e613d799385916ed8f3ea382ca",
                            "link": null,
                            "title": "topic can add multiple comments",
                            "note": null,
                            "label": null,
                            "comment": "I'm a comment!\nHello comment!",
                            "markers": []
                        }
                    ]
                },
                {
                    "id": "e613d799385919451116404d66",
                    "link": null,
                    "title": "topic with \n notes",
                    "note": "notes for this topic",
                    "label": null,
                    "comment": null,
                    "markers": [
                        "priority-3"
                    ]
                },
                {
                    "id": "e613d7993859156671fa2c12a5",
                    "link": "file:///Users/zhangchuzhao/Project/python/tmp/xmind/example/xminddemo/logo.png",
                    "title": "topic with a file",
                    "note": null,
                    "label": null,
                    "comment": null,
                    "markers": [
                        "priority-4"
                    ]
                }
            ]
        }
    }
]
(2)将画布转换为 Dict 数据
import xmind
workbook = xmind.load('demo.xmind')
sheet = workbook.getPrimarySheet()
print(sheet.getData())


Output:

{
    "id": "2cc3b068922063a81a20029655",
    "title": "first sheet",
    "topic": {
        "id": "2cc3b06892206f95288e487b6c",
        "link": null,
        "title": "root node",
        "note": null,
        "label": null,
        "comment": null,
        "markers": [],
        "topics": [
            {
                "id": "2cc3b06892206c816e1cb55ddc",
                "link": null,
                "title": "first sub topic",
                "note": null,
                "label": null,
                "comment": null,
                "markers": [],
                "topics": [
                    {
                        "id": "b0ed74214dbca939935b981906",
                        "link": null,
                        "title": "I'm a sub topic too",
                        "note": null,
                        "label": null,
                        "comment": null,
                        "markers": []
                    }
                ]
            },
            {
                "id": "b0ed74214dbca693b947ef03fa",
                "link": null,
                "title": "second sub topic",
                "note": null,
                "label": null,
                "comment": null,
                "markers": []
            },
            {
                "id": "b0ed74214dbca1fe9ade911b94",
                "link": null,
                "title": "third sub topic",
                "note": null,
                "label": null,
                "comment": null,
                "markers": []
            },
            {
                "id": "b0ed74214dbcac00c0eb368b53",
                "link": null,
                "title": "fourth sub topic",
                "note": null,
                "label": null,
                "comment": null,
                "markers": []
            }
        ]
    }
}
(3) 将主题转换为 Dict 数据
import xmind
workbook = xmind.load('demo.xmind')
sheet = workbook.getPrimarySheet()
root_topic = sheet.getRootTopic()
print(root_topic.getData())


Output:

{
    "id": "2cc3b06892206f95288e487b6c",
    "link": null,
    "title": "root node",
    "note": null,
    "label": null,
    "comment": null,
    "markers": [],
    "topics": [
        {
            "id": "2cc3b06892206c816e1cb55ddc",
            "link": null,
            "title": "first sub topic",
            "note": null,
            "label": null,
            "comment": null,
            "markers": [],
            "topics": [
                {
                    "id": "b0ed74214dbca939935b981906",
                    "link": null,
                    "title": "I'm a sub topic too",
                    "note": null,
                    "label": null,
                    "comment": null,
                    "markers": []
                }
            ]
        },
        {
            "id": "b0ed74214dbca693b947ef03fa",
            "link": null,
            "title": "second sub topic",
            "note": null,
            "label": null,
            "comment": null,
            "markers": []
        },
        {
            "id": "b0ed74214dbca1fe9ade911b94",
            "link": null,
            "title": "third sub topic",
            "note": null,
            "label": null,
            "comment": null,
            "markers": []
        },
        {
            "id": "b0ed74214dbcac00c0eb368b53",
            "link": null,
            "title": "fourth sub topic",
            "note": null,
            "label": null,
            "comment": null,
            "markers": []
        }
    ]
}
(4) 自定义解析
import xmind
workbook = xmind.load('demo.xmind')
custom_parse_xmind(workbook)


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)


Output:

 Sheet : 'first sheet'
     RootTopic : 'root node'
         AttachedSubTopic : 'first sub topic'
         AttachedSubTopic : 'second sub topic'
         AttachedSubTopic : 'third sub topic'
         AttachedSubTopic : 'fourth sub topic'
         DetachedSubtopic : 'detached topic'
 Sheet : 'second sheet'
     RootTopic : 'root node'
         AttachedSubTopic : 'redirection to the first sheet'
         AttachedSubTopic : 'topic with an url hyperlink'
         AttachedSubTopic : 'topic with 
 notes'
         AttachedSubTopic : 'topic with a file'
Relationship: [redirection to the first sheet] --> [topic with an url hyperlink]

具体代码参考:parse_xmind.py

3、更新保存 XMind 文件

(1)五种保存方法
import xmind
# 加载XMind文件demo.xmind
workbook = xmind.load('demo.xmind')  
primary_sheet = workbook.getPrimarySheet()
root_topic = primary_sheet.getRootTopic()
# 给中心主题添加一个星星图标
root_topic.addMarker(MarkerId.starRed)

# 第1种:默认保存所有的内容,这里保存时另存为xmind_update_demo.xmind(推荐)
xmind.save(workbook=workbook, path='xmind_update_demo.xmind')

# 第2种:只保存思维导图内容content.xml核心文件,适用于没有添加评论、自定义样式和附件的情况
xmind.save(workbook=workbook, path='xmind_update_demo1.xmind', only_content=True)

# 第3种:只保存content.xml、comments.xml、styles.xml三个核心文件,适用于没有附件的情况
xmind.save(workbook=workbook, path='xmind_update_demo2.xmind', except_attachments=True)

# 4、除了修改记录,其他内容都保存,因为XMind文件的修改记录文件夹比较大,以便节约内存(推荐)
xmind.save(workbook=workbook, path='xmind_update_demo3.xmind', except_revisions=True)

# 5、不指定保存路径,直接更新原文件
xmind.save(workbook)

具体代码参考:update_xmind.py

(2)XMind 文件结构

xmind file structure

四、工具支持功能

1、支持 XMind 以下原生元素的创建、解析和更新

  • 画布 (Sheet)
  • 主题 (Topic:固定主题、自由主题)
  • 图标 (Marker:图标名称)
  • 备注 (Note)
  • 标签 (Label)
  • 批注 (Comment)
  • 联系 (Relationship)
  • 样式 (Styles)

2、XMind 原生元素

xmind_native_elements

其中,暂不支持的元素(日常也比较少用到)

  • 标注(cllout topic)
  • 概要(summary topic)
  • 外框(outline border)
  • 附件

五、应用场景

XMind2TestCase:一个高效测试用例设计的解决方案!

该方案通过制定测试用例通用模板, 然后使用 XMind 这款广为流传且开源的思维导图工具进行用例设计。

然后基于通用的测试用例模板,在 XMind 文件上解析并提取出测试用例所需的基本信息, 合成常见测试用例管理系统所需的用例导入文件。

实现将 XMind 设计测试用例的便利与常见测试用例系统的高效管理完美结合起来了,提升日常测试工作的效率!

使用流程如下:

1、使用 Web 工具进行 XMind 用例文件解析

webtool

2、转换后的用例预览

testcase preview

3、用例导入 TestLink 系统

testlink

4、用例导入 Zentao(禅道)系统

zentao

六、自动化测试与发布

1、自动化单元测试 (TODO: 待上传)

python3 -m unittest discover

2、一键打 Tag 并上传至 PYPI

每次在 __ about __.py 更新版本号后,运行以下命令,实现自动化更新打包上传至 PYPI ,同时根据其版本号自动打 Tag 并推送到仓库:

python3 setup.py pypi

upload pypi

七、致谢

在此,衷心感谢 XMind 思维导图官方创造了这么一款激发灵感、创意,提升工作、生活效率的高价值生产力产品,
同时还开源 xmind-sdk-python 工具帮助开发者构建自己的 XMind 文件 ,本项目正是基于此工具进行扩展和升级,受益匪浅,感恩!

得益于开源,也将坚持开源,并为开源贡献自己的点滴之力。后续,将继续根据实际项目需要,定期进行维护更新和完善,欢迎大伙的使用和意见反馈,谢谢!

(如果本项目对你有帮助的话,也欢迎 star

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 19 条回复 时间 点赞

这个厉害

Pangluo 回复

欢迎使用和建议唷

赞,喜欢用 XMind 思维导图进行测试用例设计

支持导入到 JIRA 么~~

zlp 回复

这边也是使用 Jira 进行日常项目管理和排期,你说的支持 Jira 指的是?

赞 1 个,这个很有用

胖虎 回复

到时有更多使用场景,也一起分享分享

当放在 testcase 的子主题下,还是无法识别测试步骤。但是用您给的 xmind 的用例说明,复制粘贴到自己的用例时,就可以识别出来。请问这是为什么呢?

Edrain 回复

TestCase 是通过优先级标识,如果没有优先级标识,整条用作为测试标题

试用了一下还蛮不错的,另外想问一下能不能把转换成 excel?想直接导入到 jira 系统里

Devin 回复

jira 不是也有用例管理的么~~

Devin #13 · 2019年01月07日 Author
zlp 回复

可以的,转换为 Excel,很简单,直接我另一个工具的用例数据接口即可,地址是:https://github.com/zhuifengshen/xmind2testcase

Devin #14 · 2019年01月07日 Author
zlp 回复

还没用 jira 的用例管理功能,改天试试看


这功能在哪找?

这个赞,自研平台用例管理可以跟 xmind 打通

blackFheaven 回复

这个地址里https://github.com/zhuifengshen/xmind2testcase
作者没有明说,看仔细点就可以了,

导出 zentao 的格式为 csv,可是禅道并不接受 csv 格式的文档,这个问题有小伙伴解决了吗?

web 转换页面为什么打不开。提示 404,但是现实启动成功

前置条件没有设定吗?转换后没显示前置条件?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册