Qt Drag and drop between QTreeWidget

后端 未结 1 1538

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 child
1条回答
  •  星月不相逢
    2021-01-14 07:55

    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
    
    • From A to A move items with all childrens.

    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)
    
    • From B to A copy items with all childrens.

    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

    0 讨论(0)
提交回复
热议问题