本文介绍了Qt 在 QTreeWidget 之间拖放的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的 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 之间拖放的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-21 15:24