31. QML仿制思维导图系列02 -- 绘制初始三角形

本文是QML仿制思维导图系列的第二部分,主要介绍了如何在QML中创建一个跟随节点移动的三角形。通过在NodeItem.qml中定义绘制函数和响应鼠标移动的函数,实现了节点移动时三角形同步更新的效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 说明

本系列文章最终目的是利用QML设计一个类似思维导图的小软件,可以基本实现节点之间的连线问题
第一版最终效果:

思维导图第一版最终效果


本篇文章在上篇文章的基础上继续开发,增加 初始三角形绘制 功能。上篇文章为: QML仿制思维导图系列01 – 搭建基本框架

2. 基本步骤

大致思路就是在产生节点时,要首先在节点的右侧绘制一个三角形,此三角形为起初的三角形,当节点本身被鼠标拖拽移动后,要实时的对画布绘图区域进行更新,同时更改三角形的绘制起始点,以使三角形跟随节点移动。

<think>我们正在使用QML构建思维导图,其中包含曲线(可能用Canvas或QtCharts绘制)和节点(MindMapNode.qml)。删除操作需要分别处理曲线和节点。根据之前讨论,删除曲线有两种情况:1.若使用QtCharts(如SplineSeries),则通过chartView.removeSeries(series)或series.destroy()2.若使用Canvas,则清除画布并重绘(可能需要记录绘制命令以便重绘时排除被删除的曲线)对于节点(MindMapNode.qml),我们通常将其作为Item动态创建,删除时调用节点的destroy()方法。然而,实际思维导图中,节点和曲线可能是关联的。因此,删除节点时通常需要同时删除与之相连的曲线。实现步骤:假设我们有一个思维导图的数据模型,每个节点有一个唯一标识符(如uuid),曲线也记录其连接的起点和终点节点。步骤1:删除节点-QML中,节点可能是动态创建的,我们将其引用存储在一个容器中(如一个Map,以节点id为键)。-当删除节点时,从容器中取出该节点对象,调用destroy()方法将其销毁。-同时,从数据模型中移除该节点的数据。步骤2:删除与该节点相连的曲线-遍历曲线集合,找到所有连接该节点的曲线。-对于每条曲线:如果是QtCharts的Series,则从ChartView中移除并销毁。如果是Canvas绘制,则从绘制的曲线列表中移除,并清除画布后重新绘制剩余曲线(或者使用局部擦除,但通常重绘更简单)。步骤3:更新数据模型-从数据模型中移除节点和关联的曲线数据。具体代码结构示例:1.节点删除函数(在管理节点的QML文件中):```qmlfunctionremoveNode(nodeId){//获取节点对象varnode=nodeMap[nodeId]if(node){//1.删除与该节点相连的曲线removeCurvesConnectedToNode(nodeId)//2.销毁节点node.destroy()//3.从节点Map中移除deletenodeMap[nodeId]//4.从数据模型中移除节点数据dataModel.removeNode(nodeId)//假设dataModel有removeNode方法}}```2.删除连接曲线的函数:```qmlfunctionremoveCurvesConnectedToNode(nodeId){//假设curvesModel存储了所有曲线,每条曲线有fromNodeId和toNodeIdvarcurvesToRemove=[]for(vari=0;i<curvesModel.count;i++){varcurve=curvesModel.get(i)if(curve.fromNodeId===nodeId||curve.toNodeId===nodeId){curvesToRemove.push(curve.curveId)}}//遍历要删除的曲线ID,执行删除curvesToRemove.forEach(function(curveId){//从图表或画布中删除曲线if(usingQtCharts){varseries=curveSeriesMap[curveId]chartView.removeSeries(series)series.destroy()}elseif(usingCanvas){//从Canvas的曲线数据中移除removeCurveFromCanvasData(curveId)}//从曲线模型中移除curvesModel.removeCurve(curveId)})}```3.对于Canvas的情况,我们需要重新绘制:```qmlfunctionremoveCurveFromCanvasData(curveId){//从存储曲线数据的数组中移除for(vari=0;i<canvasCurves.length;i++){if(canvasCurves[i].curveId===curveId){canvasCurves.splice(i,1)break}}//请求重绘画布mindMapCanvas.requestPaint()}```然后在Canvas的onPaint中,根据canvasCurves数组重新绘制所有曲线。注意事项:-动态创建的对象(节点和曲线)必须妥善管理其生命周期,避免内存泄漏。-删除操作可能涉及多个步骤,建议使用Qt.callLater来避免在遍历过程中修改数据结构。-如果使用QtCharts,注意删除系列后,图表的更新是立即的。对于节点(MindMapNode.qml),我们通常这样动态创建:```qmlComponent{id:nodeComponentMindMapNode{//属性绑定...}}functioncreateNode(properties){varnode=nodeComponent.createObject(mindMapContainer,properties)if(node){nodeMap[node.nodeId]=node}returnnode}```总结:删除操作需要同时删除节点对象和关联的曲线,并更新数据模型。相关问题:1.QML中如何管理动态创建的对象以防止内存泄漏?2.如何优化Canvas重绘性能以避免在大量曲线时卡顿?3.QtCharts中删除多个系列的最佳实践是什么?4.如何实现思维导图的撤销/重做功能以支持删除操作的撤销?</think>### QT5.14 QML 删除思维导图曲线及节点实现方法 在QT5.14 QML中删除思维导图的曲线和节点,需要根据创建方式采用不同的删除策略。以下是完整解决方案: #### 一、删除思维导图曲线 根据曲线的实现方式选择对应方法: 1. **使用QtCharts模块的情况** 若曲线是通过`QSplineSeries`或`QLineSeries`实现: ```qml // 删除单个曲线系列 chartView.removeSeries(mindCurveSeries) mindCurveSeries.destroy() // 批量删除所有曲线 for (var i = chartView.series.length - 1; i >= 0; i--) { chartView.removeSeries(chartView.series[i]) } ``` 2. **使用Canvas绘制的情况** 若曲线是通过Canvas手动绘制: ```qml // 清除整个画布 function clearCanvas() { var ctx = mindMapCanvas.getContext('2d') ctx.reset() mindMapCanvas.requestPaint() } // 选择性删除特定曲线(需记录绘制路径) function removeSpecificCurve(curveId) { curveData = curveData.filter(item => item.id !== curveId) redrawAllCurves() // 重绘剩余曲线 } ``` #### 二、删除MindMapNode.qml生成的节点 节点通常是动态创建的Item,删除方法如下: ```qml // 存储节点的Map property var nodeMap: ({}) // 创建节点 function createNode(properties) { var node = Qt.createComponent("MindMapNode.qml").createObject(parent, properties) nodeMap[node.nodeId] = node // 假设节点有唯一ID } // 删除单个节点 function removeNode(nodeId) { if (nodeMap[nodeId]) { nodeMap[nodeId].destroy() // 销毁节点对象 delete nodeMap[nodeId] // 移除引用 } } // 批量删除所有节点 function clearAllNodes() { for (var id in nodeMap) { nodeMap[id].destroy() } nodeMap = {} } ``` #### 三、关联数据模型的处理 若节点/曲线绑定数据模型,需同步删除数据源: ```qml ListModel { id: nodeDataModel } // 删除时同步操作模型 function removeNodeAndData(nodeId) { for (var i = 0; i < nodeDataModel.count; i++) { if (nodeDataModel.get(i).id === nodeId) { nodeDataModel.remove(i) removeNode(nodeId) // 调用前述节点删除函数 break } } } ``` #### 四、优化建议 1. **内存管理** 销毁对象后调用`gc()`触发垃圾回收: ```qml removeNode(nodeId) Qt.callLater(gc) // 延迟执行回收 ``` 2. **动画效果** 添加删除动画提升体验: ```qml SequentialAnimation { id: deleteAnim PropertyAnimation { target: node property: "opacity" to: 0 duration: 300 } ScriptAction { script: node.destroy() } } ``` 3. **批量操作优化** 使用`Qt.callLater`分批处理: ```qml function batchRemoveNodes(nodeIds) { var index = 0 function processNext() { if (index < nodeIds.length) { removeNode(nodeIds[index++]) Qt.callLater(processNext) } } Qt.callLater(processNext) } ``` > **关键提示**: > - 动态创建的对象必须显式调用`destroy()`[^1] > - 删除后立即更新视图避免残留渲染 > - 复杂场景建议使用`QtObject`管理生命周期[^1] --- ### 相关问题 1. QML中如何实现思维导图节点的拖拽和位置保存功能? 2. QtCharts模块中如何实现曲线的高亮和交互式选择? 3. 动态创建大量QML组件时如何优化内存管理? 4. 如何为思维导图添加撤销/重做(Undo/Redo)功能支持删除操作的撤销?[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山间点烟雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值