qml实现Json可视化(头脑风暴、思维导图)

本demo来自 https://github.com/cjmdaixi/QuickJSONViewer

代码上传到了 https://download.csdn.net/download/u011942101/85005519

Json杂谈

JSON是一个序列化的对象或数组,是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
很多Json数据解析工具。例如在线的 [https://www.sojson.com/simple_json.html],
在这里插入图片描述
此demo确实用图表的方式来展示Json结构,通过借鉴此代码,也可以快速的实现类似Xml等数据的图标展示。效果如图:
在这里插入图片描述

实现的思路

核心问题包括下面几个方面:

  1. 对JSON数据进行解析,我使用的是递归方法,比较容易理解。
  2. 同层级的节点放进一个ColumnLayout中,轻松实现均匀排布。
  3. 对JSON的数据类型需要判断,在JS中不好做,所以放在C++中实现,这里就涉及到如何在C++中构造QML对象以及设置属性。

核心代码

递归创建节点
para1 : 父节点 ,para2 : 节点的文本, para3 : josn数据 ,pare4 : 层级关系


QQuickItem * FluxHub::createObjectNode(QQuickItem *currentItem, const QString &key, const QJsonObject &jsonObj, int level)
{
    auto objectNode = createLevelItem(level, currentItem);
    setNodeText(objectNode, key);

    for(auto it = jsonObj.begin(); it != jsonObj.end(); ++it){
        auto child = it.value();
        auto childName = it.key();
        auto childType = it.value().type();

        QQuickItem *rt = nullptr;
        if(childType == QJsonValue::Object){
            rt = createObjectNode(objectNode, childName, child.toObject(), level + 1);

        }
        else if(childType == QJsonValue::Array){
            rt = createArrayNode(objectNode, childName, child.toArray(), level + 1);
        }
        else{
            rt = createLeafNode(objectNode, childName, child, level + 1);
        }
        if(rt == nullptr){
            qWarning()<<"Create child node error!"<<childName<<childType;
            return nullptr;
        }
    }
    return objectNode;
}
QQuickItem *FluxHub::createLevelItem(int level, QQuickItem *parentItem)
{
    auto *component = level >= m_levelComponents.count()?
                m_levelComponents.back() : m_levelComponents[level];
    auto item = qobject_cast<QQuickItem*>(component->create());

    if(item == nullptr){
        qWarning()<<"Create level item error!"<<component->errorString();
        return nullptr;
    }
    QQmlProperty layoutProp(parentItem, "childrenLayout");
    if(layoutProp.type() == QQmlProperty::Invalid){
        item->setParentItem(parentItem);
    }else{
        auto layout = layoutProp.read().value<QQuickItem*>();
        Q_ASSERT(layout);
        item->setParentItem(layout);
    }

    QQmlProperty colorProp(item, "color");
    colorProp.write(levelColor(level));
    return item;
}

//节点Node.qml

import QtQuick 2.7
import QtQuick.Layouts 1.3

Item{
    id: root
    implicitWidth: childrenRect.width
    implicitHeight: childrenRect.height
    property alias nodeRect: nodeContentRect
    property alias text: nodeText.text
    property alias childrenLayout: childrenLayout
    property string color
    RowLayout {
        id: rowLayout

        spacing: 50

        Rectangle{
            id: nodeContentRect
            implicitWidth: nodeText.contentWidth + 15
            implicitHeight: nodeText.contentHeight + 15
            color: root.color
            radius: 5
            Text{
                id: nodeText
                anchors.fill: parent
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
                font{
                    family: "Arial"
                    pointSize: 15
                }
                color: "white"
            }
        }

        ColumnLayout{
            id: childrenLayout
            spacing: 10
            onHeightChanged: lineCanvas.requestPaint();
            onWidthChanged: lineCanvas.requestPaint();
        }
    }
    Canvas{
        id: lineCanvas
        anchors.fill: parent
        onPaint: {
            var ctx = getContext("2d");
            ctx.reset();
            var pt1 = mapFromItem(nodeContentRect, nodeContentRect.width, nodeContentRect.height / 2);
            for(var i = 0; i !== childrenLayout.children.length; ++i){
                var item = childrenLayout.children[i];
                var pt2 = mapFromItem(item.nodeRect, 0, item.nodeRect.height / 2);
                ctx.moveTo(pt1.x, pt1.y);
                //ctx.lineTo(pt2.x, pt2.y);
                ctx.bezierCurveTo(pt1.x + 20, pt1.y, pt2.x - 20, pt2.y, pt2.x, pt2.y);
            }
            ctx.strokeStyle = "#969f95";
            ctx.lineWidth = 2;
            ctx.stroke();
        }
    }
}

从中学习到了什么

  1. c++ 中构造Qml对象,相关类 QQmlComponent
  2. c++中读取qml对象的属性,比如
QQmlProperty layoutProp(parentItem, "childrenLayout");
if(layoutProp.type() == QQmlProperty::Invalid){
    item->setParentItem(parentItem);
}else{
    auto layout = layoutProp.read().value<QQuickItem*>();
    Q_ASSERT(layout);
    item->setParentItem(layout);
}
QQmlProperty colorProp(item, "color");
colorProp.write(levelColor(level));
  1. qml Canvas 画线,位置的定位
 Canvas{
        id: lineCanvas
        anchors.fill: parent
        onPaint: {
            var ctx = getContext("2d");
            ctx.reset();
            var pt1 = mapFromItem(nodeContentRect, nodeContentRect.width, nodeContentRect.height / 2);
            for(var i = 0; i !== childrenLayout.children.length; ++i){
                var item = childrenLayout.children[i];
                var pt2 = mapFromItem(item.nodeRect, 0, item.nodeRect.height / 2);
                ctx.moveTo(pt1.x, pt1.y);
                //ctx.lineTo(pt2.x, pt2.y);
                ctx.bezierCurveTo(pt1.x + 20, pt1.y, pt2.x - 20, pt2.y, pt2.x, pt2.y);
            }
            ctx.strokeStyle = "#969f95";
            ctx.lineWidth = 2;
            ctx.stroke();
        }
    }

感谢无私奉献的人

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Cascader 算法是一种基于 Trie 树的前缀匹配算法,用于快速查找字符串的匹配项。在 QML 中,我们可以使用 JSON 数据结构来实现 Cascader 算法。 首先,我们需要定义一个 JSON 对象来存储 Cascader 树的结构。下面是一个简单的例子: ```json { "label": "root", "children": [ { "label": "a", "children": [ { "label": "aa", "value": "aa" }, { "label": "ab", "value": "ab" } ] }, { "label": "b", "children": [ { "label": "ba", "value": "ba" }, { "label": "bb", "value": "bb" } ] } ] } ``` 这个 JSON 对象表示了一个 Cascader 树,根节点的 label 为 "root",有两个子节点 "a" 和 "b"。每个子节点又有自己的子节点,最后的叶子节点包含了一个 value 值,用于表示匹配项。 接下来,我们可以使用 QML 中的 ListView 组件来实现 Cascader 界面。ListView 的 model 可以绑定到 Cascader 树的 children 属性,用于显示每个节点的 label。当用户选择某个节点时,我们可以递归遍历 Cascader 树,查找匹配项并显示在界面上。 下面是一个简单的 QML 代码示例: ```qml import QtQuick 2.0 Rectangle { width: 400 height: 400 ListView { id: listView anchors.fill: parent model: cascader.children delegate: Component { Text { text: model.label font.bold: true font.pointSize: 15 width: parent.width height: 30 verticalAlignment: Text.AlignVCenter MouseArea { anchors.fill: parent onClicked: { select(model) } } } } function select(node) { if (node.value !== undefined) { console.log("Selected value:", node.value) } else { listView.model = node.children } } } Component.onCompleted: { listView.model = cascader.children } property var cascader: { "label": "root", "children": [ { "label": "a", "children": [ { "label": "aa", "value": "aa" }, { "label": "ab", "value": "ab" } ] }, { "label": "b", "children": [ { "label": "ba", "value": "ba" }, { "label": "bb", "value": "bb" } ] } ] } } ``` 这个示例中,我们使用了一个 Rectangle 组件作为 Cascader 界面的根节点,并在其中添加了一个 ListView 组件。ListView 的 model 绑定到 Cascader 树的 children 属性,delegate 定义了每个节点的显示方式。当用户选择某个节点时,我们调用 select 函数来处理选择事件。如果选择的节点包含 value 属性,则表示已经找到匹配项,直接输出 value 值。否则,我们将 ListView 的 model 改成选择的节点的 children 属性,继续显示下一级节点。 这样,我们就成功地实现了一个简单的 Cascader 界面,并使用 JSON 数据结构来存储 Cascader 树的结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值