PyQt 项目视图自定义拖放

PyQt item view custom drag and drop

我正在 QTableView 中实现自定义拖放。当我拖动一个单元格并将其放到另一个单元格上时,我想根据拖动的内容和放置的位置手动更改模型中的一些数据。我怎样才能做到这一点?我一直在阅读所有 Qt 文档,但我完全迷路了,特别是拖放时,C++ 到 PyQt 的转换似乎不太直观。

基本上我需要的是当我放下时我想知道最初拖动了哪些单元格,以及它们被放下的位置。我的困惑似乎在于 QMimeData。据我所知,拖动开始时,拖动事件接收到正确的 MIME 数据,但我不知道如何在 PyQt 中获取它(过去能够用文本和 url 做这种事情,但我'当涉及到项目视图时,我迷路了)。我还需要知道我要去哪里。我想我可以做一个 "item at cursor pos" 但我假设这个数据已经存在于 drop 事件中,我只需要弄清楚如何查询它。

这是一个简单的例子:

import sys
from PyQt4 import QtGui, QtCore


class TableView(QtGui.QTableView):
     def __init__(self, parent=None):
         QtGui.QTreeWidget.__init__(self, parent)
         self.setDragEnabled(True)
         self.setDropIndicatorShow(True)
         self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
         self.setDragDropOverwriteMode(False)
         self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)

     def dragEnterEvent(self, event):
        event.accept()

    def dropEvent(self, event):
        # I want to do cool things with the dragged cells, and I need to know where they dropped!
        print(event.mimeData().formats()) # this tells me that I shuld get some sort of "qabstractitemmodeldatalist". Sounds promising...
        print(event.mimeData().data("application/x-qabstractitemmodeldatalist")) # this gives me an interesting looking QByteArray but I have no idea what to do with it...
        event.accept()

class Dialog(QtGui.QDialog):
    def __init__(self):
        super(Dialog, self).__init__()

        model = QtGui.QStandardItemModel(self)
        model.insertRow(0, QtGui.QStandardItem("C"))
        model.insertRow(0, QtGui.QStandardItem("B"))
        model.insertRow(0, QtGui.QStandardItem("A"))

        table = TableView(self)
        table.setModel(model)

app = QtGui.QApplication(sys.argv)
ex = Dialog()
ex.show()
sys.exit(app.exec_())

你不知道它被拖到哪里,因为 mimeData 没有该信息,但你可以得到被拖动的数据,为此我们创建了一个临时模型,我们将在其中建立模拟相同拖动行为的 mimeData。要获取它被丢弃的位置,作为事件一部分的位置必须与 indexAt() 一起使用,从而获得 QModelIndex:

import sys
from PyQt4 import QtGui, QtCore


class TableView(QtGui.QTableView):
    def __init__(self, parent=None):
        QtGui.QTreeWidget.__init__(self, parent)
        self.setDragEnabled(True)
        self.setDropIndicatorShown(True)
        self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
        self.setDragDropOverwriteMode(False)
        self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)

    def dragEnterEvent(self, event):
        event.accept()

    def dropEvent(self, event):
        if self.viewport().rect().contains(event.pos()):
            fake_model = QtGui.QStandardItemModel()
            fake_model.dropMimeData(
                event.mimeData(), event.dropAction(), 0, 0, QtCore.QModelIndex()
            )
            print("from:")
            for r in range(fake_model.rowCount()):
                for c in range(fake_model.columnCount()):
                    ix = fake_model.index(r, c)
                    print(ix.data())
            to_index = self.indexAt(event.pos())
            if to_index.isValid():
                print("to:", to_index.data())
        super(TableView, self).dropEvent(event)


class Dialog(QtGui.QDialog):
    def __init__(self):
        super(Dialog, self).__init__()

        model = QtGui.QStandardItemModel(self)
        for letter in "ABC":
            model.appendRow(QtGui.QStandardItem(letter))

        table = TableView()
        table.setModel(model)

        lay = QtGui.QVBoxLayout(self)
        lay.addWidget(table)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    ex = Dialog()
    ex.show()
    sys.exit(app.exec_())