问题描述
在我的 QT 应用程序中,我有两个 QTreeWidget A 和 B.
我想为这两种行为重写拖放功能:
- 从 A 到 A 移动包含所有子项的项目.
- 从 B 到 A 复制包含所有子项的项目.
我认为在 dropEvent 函数中我有 event.source() 作为获取源.
但是如何使用标志来改变功能以进行移动或动态复制?
有例子吗?
谢谢
为了执行这个任务,我们必须区分动作是来自他自己还是来自另一个小部件,因为它被 dropEvent
方法覆盖了,这将返回一个 QDropEvent
类型的事件,该事件具有返回执行操作的对象的方法 source()
:
def dropEvent(self, event):如果 event.source() == self:# 移动别的:# 复制
- 从 A 到 A 移动包含所有子项的项目.
每个QTreeWidgetItem
都和它的父级相关联,所以如果它移动到父级,子级也会移动.
if event.source() == self:event.setDropAction(Qt.MoveAction)QTreeWidget.dropEvent(self, event)
- 从 B 到 A 复制包含所有子项的项目.
这是最复杂的任务,因为当 QTreeWidget
使用拖放时,它使用 mimetype x-qabstractitemmodeldatalist
存储关联到的行和列的信息项目的 QModelIndex 加上带有角色及其值的字典:
row_1 |列_1 |有效角色数 |角色_1_1 |值_1_1 |... |role_n_1 |value_n_1 |行_2 |列_2 |...
所以知道行和列是不可能获得项目的,因此也无法获得你的孩子,因为你必须有父母.所以你应该寻找另一种策略,解决方案是创建一个新的mimetype,在其中保存再次获取项目所需的信息,唯一的方法是保存项目的行,然后是父项的行,然后像这样的父母的父母行,直到您到达某个topLevelItem
.
由于您已经拥有该项目,您可以访问您的孩子及其包含的信息.完整的实现如下所示:
class TreeWidget(QTreeWidget):customMimeType = "application/x-customTreeWidgetdata"def mimeTypes(self):mimetypes = QTreeWidget.mimeTypes(self)mimetypes.append(TreeWidget.customMimeType)返回 mimetypesdef startDrag(self, supportedActions):拖动 = QDrag(self)mimedata = self.model().mimeData(self.selectedIndexes())编码 = QByteArray()流 = QDataStream(编码,QIODevice.WriteOnly)self.encodeData(self.selectedItems(), 流)mimedata.setData(TreeWidget.customMimeType,编码)drag.setMimeData(mimedata)drag.exec_(supportedActions)def dropEvent(self, event):如果 event.source() == self:event.setDropAction(Qt.MoveAction)QTreeWidget.dropEvent(self, event)elif isinstance(event.source(), QTreeWidget):如果 event.mimeData().hasFormat(TreeWidget.customMimeType):编码 = event.mimeData().data(TreeWidget.customMimeType)parent = self.itemAt(event.pos())items = self.decodeData(encoded, event.source())对于它在项目中:item = QTreeWidgetItem(父)self.fillItem(it, item)self.fillItems(it, item)event.acceptProposedAction()def fillItem(self, inItem, outItem):对于范围内的 col(inItem.columnCount()):对于范围内的键(Qt.UserRole):角色 = Qt.ItemDataRole(key)outItem.setData(col, role, inItem.data(col, role))def fillItems(self, itFrom, itTo):对于范围内的 ix(itFrom.childCount()):it = QTreeWidgetItem(itTo)ch = itFrom.child(ix)self.fillItem(ch, it)self.fillItems(ch, it)def encodeData(self, items, stream):stream.writeInt32(len(items))对于项目中的项目:p = 项目行 = []而 p 不是 None:rows.append(self.indexFromItem(p).row())p = p.parent()stream.writeInt32(len(rows))对于反向行(行):stream.writeInt32(行)返回流def decodeData(自我,编码,树):项目 = []行 = []流 = QDataStream(编码,QIODevice.ReadOnly)而不是 stream.atEnd():nItems = stream.readInt32()对于范围内的 i(nItems):路径 = stream.readInt32()行 = []对于范围(路径)中的 j:row.append(stream.readInt32())行.追加(行)对于行中的行:it = tree.topLevelItem(row[0])对于第 [1:] 行中的 ix:it = it.child(ix)items.append(it)退换货品
在下面的链接中,您将看到一个示例
In my QT application I have two QTreeWidget A and B.
I want rewrite drag and drop function for this two behavior:
- From A to A move items with all childrens.
- From B to A copy items with all childrens.
I have view that in dropEvent function I have event.source() for take source.
But how change function with flag for movement or copy dinamically?
There is an example?
Thanks
In order to perform this task we must distinguish if the action comes from himself or another widget for it is overwritten by the dropEvent
method, this returns an event of type QDropEvent
that has the method source()
that returns the object that performed the action:
def dropEvent(self, event):
if event.source() == self:
# move
else:
# copy
Each QTreeWidgetItem
is associated with its parent, so if it moves to the parent, the children will also move.
if event.source() == self:
event.setDropAction(Qt.MoveAction)
QTreeWidget.dropEvent(self, event)
This is the most complicated task because when QTreeWidget
uses drag-and-drop it uses the mimetype x-qabstractitemmodeldatalist
that stores the information of the row and column associated to the QModelIndex of the item plus a dictionary with the roles and their values:
row_1 | column_1 | number of valid roles | role_1_1 | value_1_1 | ... | role_n_1 | value_n_1 | row_2 | column_2 | ...
so knowing the row and column it is impossible to obtain the item and therefore also your children since you must have the parent. So you should look for another strategy, the solution is to create a new mimetype where you save the necessary information to obtain the item again, the only way to do it is to save the row of the item, then the row of the parent, and then the row of parent's parent like this until you reach some topLevelItem
.
Since you already have the item you can access your children and the information they contain. The full implementation is shown below:
class TreeWidget(QTreeWidget):
customMimeType = "application/x-customTreeWidgetdata"
def mimeTypes(self):
mimetypes = QTreeWidget.mimeTypes(self)
mimetypes.append(TreeWidget.customMimeType)
return mimetypes
def startDrag(self, supportedActions):
drag = QDrag(self)
mimedata = self.model().mimeData(self.selectedIndexes())
encoded = QByteArray()
stream = QDataStream(encoded, QIODevice.WriteOnly)
self.encodeData(self.selectedItems(), stream)
mimedata.setData(TreeWidget.customMimeType, encoded)
drag.setMimeData(mimedata)
drag.exec_(supportedActions)
def dropEvent(self, event):
if event.source() == self:
event.setDropAction(Qt.MoveAction)
QTreeWidget.dropEvent(self, event)
elif isinstance(event.source(), QTreeWidget):
if event.mimeData().hasFormat(TreeWidget.customMimeType):
encoded = event.mimeData().data(TreeWidget.customMimeType)
parent = self.itemAt(event.pos())
items = self.decodeData(encoded, event.source())
for it in items:
item = QTreeWidgetItem(parent)
self.fillItem(it, item)
self.fillItems(it, item)
event.acceptProposedAction()
def fillItem(self, inItem, outItem):
for col in range(inItem.columnCount()):
for key in range(Qt.UserRole):
role = Qt.ItemDataRole(key)
outItem.setData(col, role, inItem.data(col, role))
def fillItems(self, itFrom, itTo):
for ix in range(itFrom.childCount()):
it = QTreeWidgetItem(itTo)
ch = itFrom.child(ix)
self.fillItem(ch, it)
self.fillItems(ch, it)
def encodeData(self, items, stream):
stream.writeInt32(len(items))
for item in items:
p = item
rows = []
while p is not None:
rows.append(self.indexFromItem(p).row())
p = p.parent()
stream.writeInt32(len(rows))
for row in reversed(rows):
stream.writeInt32(row)
return stream
def decodeData(self, encoded, tree):
items = []
rows = []
stream = QDataStream(encoded, QIODevice.ReadOnly)
while not stream.atEnd():
nItems = stream.readInt32()
for i in range(nItems):
path = stream.readInt32()
row = []
for j in range(path):
row.append(stream.readInt32())
rows.append(row)
for row in rows:
it = tree.topLevelItem(row[0])
for ix in row[1:]:
it = it.child(ix)
items.append(it)
return items
In the following link you will see an example
这篇关于Qt 在 QTreeWidget 之间拖放的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!