在QTreeView中拖放,removeRows没有被调用

Posted

技术标签:

【中文标题】在QTreeView中拖放,removeRows没有被调用【英文标题】:Drag and drop in QTreeView, removeRows not called 【发布时间】:2014-06-16 19:18:49 【问题描述】:

我在QTreeView中的拖放有一些问题:

我将标志设置为Qt::MoveAction 并在我的模型中重新实现removeRows()dropMimeData() 等。模型继承QAbstractItemModel

当我拖放时,mimeData()dropMimeData() 会被自动调用, 还有dropMimeData() 自动调用insertRows()。但是removeRows()没有被调用,所以被拖拽的item还活着。我用谷歌搜索,但他们说他们的removeRows() 是自动调用的。

为什么我的removeRows() 不在dropMimeData() 之后调用? 我应该在dropMimeData() 中手动调用removeRows() 吗? 如果是这样,我怎么知道前一个拖动开始的QModelIndex

当开始拖动时,在mimeData(),我可以将索引保存在私有成员中,但看起来不太好。

任何建议将不胜感激。

【问题讨论】:

【参考方案1】:

简答

如果一切配置正确,则目标不应移除源项,如果执行Qt::MoveAction,则拖动的发起者应移除源项。

视图配置

QAbstractItemView(是QTreeViewQListViewQTableView、...的基类)实现QDrag操作在startDrag中的发起和结束:

if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction)
    d->clearOrRemove();

因此,当请求的删除操作(由QDrag::exec 返回)是Qt::MoveAction 时,该项目将自动删除(或由setDragDropOverwriteMode 指定清除)。

视图的重要配置选项有:

setDragDropMode:指定视图是否应该接受来自外部或仅内部项目的拖放项目。该函数相应地调用setDragEnabledsetAcceptDrops

setDragEnabled: 开启内置拖拽机制 setAcceptDrops: 启用内置 drop 机制

setDragDropOverwriteMode:指定是否应删除源项(通常在树视图中)或清除(通常在表视图中)

setDefaultDropAction:启动QDrag操作时指定的默认放置动作。

模型配置

除了视图的配置,你应该正确配置你的模型。

您应该实现模型的编辑界面,即removeRowsinsertRowsmoveRowssetData。尽管根据您的具体情况可能没有必要实现所有这些,但对于可编辑模型来说,无论如何实现它们都是一种好方法。

supportedDropActions:重新实现此函数以返回支持删除操作(默认为Qt::CopyAction)。请注意,用户可以通过按一些键在不同的支持操作之间切换。 (shift 代表 Qt::MoveActioncontrol 代表 Qt::CopyAction

supportedDragActions:如果支持的拖动操作与支持的拖放操作不同,请重新实现此功能。

示例

很好的例子是 Qt 本身的源代码。对应的Q*Widget 类(例如QTreeWidget 用于QTreeView)提供了视图和对应模型的具体实现。

【讨论】:

我遇到的另一件事:视图的selectionBehavior 必须设置为QAbstractItemView::SelectRows(并且not 设置为QAbstractItemView::SelectItems)。【参考方案2】:

我的自定义模型也有同样的问题。为视图设置 dragDropOverwriteMode=false 解决了我的问题。

【讨论】:

dragDropOverwriteMode 的默认值为 false,如在 QListView 和 QTreeView 子类中。另一方面,在 QTableView 子类中,该属性已设置为 true【参考方案3】:

我认为是的,如果Qt::DropActionQt::MoveAction,则您必须从dropMimeData() 调用removeRows(),即您将树节点从一个地方完全移动到另一个地方。

写下您的第二个问题,您可以在QAbstractItemModel::mimeData() 中创建您的自定义 mime 数据,并在那里对您拖动的节点的初始信息进行编码。所以,在dropMimeData()函数中你可以解码并使用它。

【讨论】:

感谢您的快速回复。我知道对自定义 mime 数据进行编码/解码,但我无法对拖动节点的索引进行编码。如何将模型索引编码为 QDataStream? @user18640,例如,可以存储行号和列号,或者树项路径或者其他可以唯一标识模型索引的信息。 我明白了,我将对行、列和模型索引内部 ID 进行编码,以在 dropMimeData() 中使用 QAbstractItemModel::createIndex()。谢谢。 如果拖放在两个不同模型之间执行会怎样? 'removeRows' 实际上应该在源模型上调用。文档说,当“dropMimeData”返回 true 时,“removeRows”将被自动调用。 这是错误的,当你从dropMimeData()返回true时,Qt会自动为你调用removeRows

以上是关于在QTreeView中拖放,removeRows没有被调用的主要内容,如果未能解决你的问题,请参考以下文章

在 PyQt 中支持拖放的 QTreeView

QTreeView - 如何判断拖放事件是重新排序还是父母之间的移动?

在 NSView 中拖放

在 Quasar 中拖放文件

在 Tkinter 中拖放?

在 UITextView 中拖放时显示文本光标