如何在Qt3D(QML)中实现撤销/重做(Undo/Redo)操作
实现方案
1. 删除指定Entity
你当前通过NodeInstantiator绑定ListModel生成实体,实体的生命周期完全和Model条目对应,删除指定实体只需操作Model即可。
如果需要实现点击选中删除的能力,只需要在实体的delegate中增加拾取组件:
delegate: Entity { id: sphereEntity components: [ SphereMesh { id:sphereMesh radius: 0.3 }, PhongMaterial { id: materialSphere ambient:"red" }, Transform { id: transform translation:Qt.vector3d(x, y, z) }, // 新增拾取组件 ObjectPicker { onClicked: { // NodeInstantiator会为每个delegate自动附加index属性,对应Model中的条目索引 entityModel.remove(sphereEntity.index) // 如果需要支持撤销删除操作,可在此处将删除的条目存入撤销栈,同步调整栈逻辑即可 } } ] }
如果只需要删除最后创建的实体,直接调用entityModel.remove(entityModel.count - 1)即可。
2. 撤销/重做功能实现
核心逻辑
用两个数组分别作为撤销栈和重做栈:
- 每次新增球体时,将本次新增的参数压入撤销栈,同时清空重做栈
- 触发
Ctrl+Z撤销时,弹出撤销栈最后一条记录压入重做栈,删除Model最后一条条目 - 触发
Ctrl+Shift+Z重做时,弹出重做栈最后一条记录压入撤销栈,将参数重新添加到Model中
代码修改
首先在RootEntity根节点下新增两个属性存储栈结构:
property var undoStack: [] property var redoStack: []
修改MouseHandler的点击新增逻辑,同步操作栈:
onPressed: { x++; let newItem = {"x":x,"y":0.0,"z": Math.random()} entityModel.append(newItem) undoStack.push(newItem) redoStack = [] // 新增操作后清空重做栈 }
在RootEntity根节点下新增快捷键处理逻辑(你的Scene3D已经开启了focus: true,可以正常接收按键事件):
Keys.onPressed: { if (event.modifiers & Qt.ControlModifier) { if (event.key === Qt.Key_Z) { if (event.modifiers & Qt.ShiftModifier) { // 重做 Ctrl+Shift+Z if (redoStack.length > 0) { let item = redoStack.pop() entityModel.append(item) undoStack.push(item) x = item.x // 同步全局x计数 } } else { // 撤销 Ctrl+Z if (undoStack.length > 0) { let item = undoStack.pop() redoStack.push(item) entityModel.remove(entityModel.count - 1) x = undoStack.length ? undoStack[undoStack.length -1].x : 0 } } event.accepted = true } } }
内容的提问来源于stack exchange,提问作者Parisa.H.R
相关产品推荐
相关产品推荐

