正常
Step 5-6:深拷贝子树 + 选中/展开态维护
Step 3-4 跑通了,但拖带子节点的父节点会发现子节点丢一半——因为 Step 3 的 clone 只复制了顶层项。这步补深拷贝,再收尾选中/展开态。
Step 5:深拷贝 cloneItem(带子节点的父节点一起搬)
目标
写一个私有 cloneItem(const QTreeWidgetItem* src),递归复制整棵子树(text/icon/toolTip/UserRole/checkState + 所有 child),替换 Step 3 里那个浅 clone。
提示
- 私有方法签名:
QTreeWidgetItem* cloneItem(const QTreeWidgetItem* src) const;(src 只读) - src 为 null 直接返回 nullptr
auto* clone = new QTreeWidgetItem;- 遍历每列复制:
setText(c, src->text(c))/setIcon/setToolTip/setData(c, Qt::UserRole, src->data(c, Qt::UserRole)) clone->setCheckState(0, src->checkState(0))- 递归 child:
for (int i = 0; i < src->childCount(); ++i)→clone->addChild(cloneItem(src->child(i))) - 回 dropMimeData,把 Step 3 的
new QTreeWidgetItem({src->text(0)})换成cloneItem(src)
关键认知
- MoveAction 下源连同子树一起删(Step 4 的 takeChild/takeTopLevelItem 删的是顶层源,它的 child 由 Qt 对象树跟着析构)。所以只要克隆完整,源端不需要单独处理 child。
检查点
拖一个有 2-3 层子节点的父节点到别处,整棵子树完整出现在新位置、原位置整棵消失 = 深拷贝对了。
对照答案
- cloneItem 递归深拷贝:
src/tree-drag-move.cpp:201-222 - dropMimeData 调 cloneItem:
src/tree-drag-move.cpp:108
Step 6:选中态 / 展开态维护 + demo 双树
目标
拖完两件事别忘:①落在节点上时自动展开目标父节点(否则新子节点看不见,用户以为没拖成功);②把选中态从已删除的源切到新落点(否则选中停在幽灵项上,下次操作崩)。
提示(在 dropEvent 末尾,删源之后)
- 判断
drop_on_item(Step 4 存的 target_parent 是否非 null) if (drop_on_item && target_parent):target_parent->setExpanded(true);+setCurrentItem(target_parent);else if (topLevelItemCount() > 0):setCurrentItem(topLevelItem(topLevelItemCount() - 1));(落空白,选最后一个顶级项)
demo 双树(验证跨树)
- demo 里建两棵 TreeDragMove,左右各一
- 两棵都开
setDragEnabled/setAcceptDrops/setDragDropMode(InternalMove) - 种子数据:左树放「项目结构」(src/tests/docs 多层),右树放「回收区」
- 左拖右 = 跨树转移(节点从左消失、出现在右)
检查点
拖完落点是折叠节点 → 节点自动展开,新子项可见 = 展开态对了。拖完后选中态停在源(已删)→ 下次键盘操作崩 = 没维护好,回头查 setCurrentItem。左树节点拖到右树、左树消失右树有 = 跨树通了。
对照答案
- 展开目标父节点 + 切选中:
src/tree-drag-move.cpp:190-198 - demo 双树 + 标志位:
demo/tree-drag-move_window.cpp:31-60 - 种子数据:
demo/tree-drag-move_window.cpp:87-110
搓完基础版,想再深一层去看 手册首页的进阶挑战:跨进程序列化、限定可拖节点、拖拽撤销、或转 QTreeView+自定义模型。卡住了翻 troubleshooting。