To get drag and drop support for the model/view framework you have to override 3 methods inside your model.
First one is the mimeTypes function. Obviously you would use a custom mime type identifier or one that is already available and widely used by many applications, Qt has support functions for them as well, check the documentation for mimetypes.
return [ "application/x-tech.artists.org" ]
Second method would be mimeData which receives a list of QModelIndex for objects you have selected and started a drag operation. Your job in this method is to pack them into a QMimeData object using a QDataStream and return it, again check the documentation when you want to use common mimetypes such as text and images.
def mimeData(self, indices):
mimeData = QtCore.QMimeData()
encodedData = QtCore.QByteArray()
stream = QtCore.QDataStream(encodedData, QtCore.QIODevice.WriteOnly)
for index in indices:
if not index.isValid():
node = index.internalPointer()
variant = QtCore.QVariant(node)
# add all the items into the stream
stream << variant
print "Encoding drag with: ", "application/x-tech.artists.org"
The final one would be dropMimeData which receives the QMimeData in the data parameter, action is the type of drag (Copy or Move), row and column is where within the parent (if it exists) you want to drop it. If you don’t use multi row multi column tree view you can just ignore the column parameter.
This last method is not final and might contain errors since I wrote it on top of my head but it should get you started with dropping for the model/view framework.
Read the comments it should be useful guidance.
def dropMimeData(self, data, action, row, column, parent):
if action == QtCore.Qt.CopyAction:
elif action == QtCore.Qt.MoveAction:
print "Param data:", data
print "Param row:", row
print "Param column:", column
print "Param parent:", parent
# Where are we inserting?
beginRow = 0
if row != -1:
print "ROW IS NOT -1, meaning inserting inbetween, above or below an existing node"
beginRow = row
print "PARENT IS VALID, inserting ONTO something since row was not -1, beginRow becomes 0 because we want to insert it at the begining of this parents children"
beginRow = 0
print "PARENT IS INVALID, inserting to root, can change to 0 if you want it to appear at the top"
beginRow = self.rowCount(QtCore.QModelIndex())
# create a read only stream to read back packed data from our QMimeData
encodedData = data.data("application/x-tech.artists.org")
stream = QtCore.QDataStream(encodedData, QtCore.QIODevice.ReadOnly)
# decode all our data back into dropList
dropList = 
numDrop = 0
while not stream.atEnd():
variant = QtCore.QVariant()
stream >> variant # extract
node = variant.toPyObject()
# add the python object that was wrapped up by a QVariant back in our mimeData method
dropList.append( node )
# number of items to insert later
numDrop += 1
print "INSERTING AT", beginRow, "WITH", numDrop, "AMOUNT OF ITEMS ON PARENT:", parent.internalPointer()
# This will insert new items, so you have to either update the values after the insertion or write your own method to receive our decoded dropList objects.
self.insertRows(beginRow, numDrop, parent)
for drop in dropList:
# If you don't have your own insertion method and stick with the insertRows above, this is where you would update the values using our dropList.
Depending on the flags you give to your QTreeView (such as InternalMove), it will automatically remove the “dragged” object from itself if its a move action so you don’t have to do that in the model, which is the reason why we don’t do anything different when doing a move vs copy. Besides the data could come from a completely different view with different model >_^
Hope this helps.