如何避免在节点上掉线

How to avoid drop on node

在我的 QtreeView 中,我使用“QStandardItemModel”来显示多个具有单独属性的项目。我想避免该项目不会混合。例如Bananas should be moveable to vegetables (same child level) but not below Asia (higher level), moving to Asia - Fruits is ok (same child level)

样本

我曾与 .itemChanged 合作过,但似乎来晚了。我需要一个信号 before 它会被丢弃,并且项目 where 它会被丢弃。我尝试了 eventFilter 并获得了

event.type() == QtCore.QEvent.DragMove: 

但是我如何获取项目将被丢弃的项目的索引以确定它在同一子级别?

为了解决这个问题,我创建了一个自定义 mimetype,它发送索引的信息和它所具有的深度级别,它只会移动那些与目标子级具有相同级别的索引。

class TreeView(QTreeView):
    customMimeType = "application/x-customqstandarditemmodeldatalist"

    def __init__(self, *args, **kwargs):
        QTreeView.__init__(self, *args, **kwargs)
        self.setSelectionMode(QAbstractItemView.SingleSelection)
        self.setDragEnabled(True)
        self.viewport().setAcceptDrops(True)
        self.setDropIndicatorShown(True)
        self.setDragDropMode(QTreeView.InternalMove)

    def itemsToPixmap(self, indexes):
        rect = self.viewport().visibleRegion().boundingRect()
        pixmap = QPixmap(rect.size())
        pixmap.fill(Qt.transparent)
        painter = QPainter(pixmap)
        for index in indexes:
            painter.drawPixmap(self.visualRect(index), self.viewport().grab(self.visualRect(index)))
        return pixmap

    def mimeTypes(self):
        mimetypes = QTreeView.mimeTypes(self)
        mimetypes.append(TreeView.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.selectedIndexes(), stream)
        mimedata.setData(TreeView.customMimeType, encoded)

        drag.setMimeData(mimedata)
        px = self.itemsToPixmap(self.selectedIndexes())
        drag.setPixmap(px)
        drag.setHotSpot(self.viewport().mapFromGlobal(QCursor.pos()) - QPoint(self.horizontalOffset(),
                                                                              self.verticalOffset()))
        drag.exec_(supportedActions)

    def encodeData(self, items, stream):
        stream.writeInt32(len(items))
        for item in items:
            p = item
            rows = []
            while p.isValid():
                rows.append(p.row())
                p = p.parent()
            stream.writeInt32(len(rows))
            for row in reversed(rows):
                stream.writeInt32(row)

    def dropEvent(self, event):
        if event.source() == self:
            if event.mimeData().hasFormat(TreeView.customMimeType):
                encoded = event.mimeData().data(TreeView.customMimeType)
                items = self.decodeData(encoded, event.source())
                ix = self.indexAt(event.pos())
                current = self.model().itemFromIndex(ix)
                p = current
                level = 1
                while p:
                    p = p.parent()
                    level += 1
                for item, ilevel in items:
                    if level == ilevel:
                        item.parent().takeRow(item.row())
                        current.appendRow(item)
                self.clearSelection()
                event.acceptProposedAction()
        else:
            event.ignore()

    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 = self.model().item(row[0])
            for r in row[1:]:
                it = it.child(r)
            items.append((it, len(row)))
        return items

完整的例子可以在下面找到link

以下更改允许将项目移动到一排子项内部,但不能移动到外部。如果目标不是同一级别,仍在更改光标。

def dropEvent(self, event):
    if event.source() == self:
        if event.mimeData().hasFormat(TreeView.customMimeType):
            encoded = event.mimeData().data(TreeView.customMimeType)
            items = self.decodeData(encoded, event.source())
            ix = self.indexAt(event.pos())
            current = self.model().itemFromIndex(ix)
            p = current
            level = 0
            while p:
                p = p.parent()
                level += 1
            for item, ilevel in items:
                if level == ilevel:
                    item.parent().takeRow(item.row())
                    current.parent().insertRow(current.row(),item)
            self.clearSelection()
            event.acceptProposedAction()
    else:
        event.ignore()