问题
I'm implementing drag and drop QTreeView based on my custom model. All works fine, my tree displays data, drag and drop is enabled and now the last step lies ahead of me - to drop and trasfer dragged data. To do this I need to implement mimeTypes, mimeData and dropMimeData methods in my model. And now my question: Is there any easy standard way how to pass an arbitrary Python object through QMimeData? I'm doing just an internal move within QTreeView which displays hierarchy of my Python classes Person. And I want to reorder them. No drag and drop outside the application, not even outside of control. I have found only single one tutorial: link text. But is it the only way? Cannot it be done without encoding the Python object into ByteArray. I need really simple solution for my only one class Person. Thank you.
回答1:
Do not try to implement drag and drop by reparenting the underlying python object. This won't work if the drag comes from outside your process; nor will it work for a copy operation (your node objects probably cannot exist in multiple places in the tree).
Think of a drag and drop "move" as three operations:
- serialize the data to some byte string
- deserialize into a new index (or new indexes)
- (optional: if "move" rather than "copy") remove the old index(es)
mineData() and dropMimeData() are the serialize and deserialize operations that you provide. Python provides some easy ways to implement them -- check the documentation for the pickle module. If you're lucky, pickle.dumps() and pickle.loads() will work out-of-the-box for you.
Edit: I couldn't figure out how to paste code in comments, so here's the solution my comment refers to. This is safe, in the sense that it will fail by throwing a KeyError instead of causing crashes if you happen to break your rules.
# drag: store off the data in a safe place, and serialize a cooky
# that the drop target can use to retrieve the data.
self.__tmp_storage_dct = { self.__tmp_storage_cooky: stuff }
m.setData(self.rowlistptr_mime_type, QByteArray(pickle.dumps(self.__tmp_storage_cooky)))
self.__tmp_storage_cooky += 1
# drop:
if mime.hasFormat(self.rowlistptr_mime_type):
print "got tmpstorage"
cooky = pickle.loads(mime.data(self.rowlistptr_mime_type).data())
nodes = self.__tmp_storage_dct.pop(cooky)
回答2:
Ok, I think I have a possible solution for you.
Keep in mind that I am a complete neophyte in this area so no warranties that his a) works b) is a decent solution c) won't make a "real" programmer toss their lunch.
What I did was convert the entire ancestor tree of a particular item into a text list of row column pairs. (i.e. list the row and column of the dragged item, the row and column of its parent, the row and column of its parent's parent, etc... till we get to an invalid index - i.e. the root)
This looks something like this (this example shows that the dragged item is four levels deep):
2;0,1;0,5;0,1,0
^ ^ ^ ^
| | | |
| | | great grandparent (and child of the root item)
| | |
| | grandparent
| |
| parent
|
item being dragged
Later, in the dropMimeData function, I reverse the list (so that it reads from the root back down to the item being dragged) and build the indexes one at a time till I get back to the originally dragged item.
Here are the snippets of code that make that all work. Again, I can't warrantee that this is a good idea, just that it appears to work and does not require that you serialize your python objects into a ByteArray.
Hope this helps.
#---------------------------------------------------------------------------
def mimeTypes(self):
"""
Only accept the internal custom drop type which is plain text
"""
types = QtCore.QStringList()
types.append('text/plain')
return types
#---------------------------------------------------------------------------
def mimeData(self, index):
"""
Wrap the index up as a list of rows and columns of each
parent/grandparent/etc
"""
rc = ""
theIndex = index[0] #<- for testing purposes we only deal with 1st item
while theIndex.isValid():
rc = rc + str(theIndex.row()) + ";" + str(theIndex.column())
theIndex = self.parent(theIndex)
if theIndex.isValid():
rc = rc + ","
mimeData = QtCore.QMimeData()
mimeData.setText(rc)
return mimeData
#---------------------------------------------------------------------------
def dropMimeData(self, data, action, row, column, parentIndex):
"""
Extract the whole ancestor list of rows and columns and rebuild the
index item that was originally dragged
"""
if action == QtCore.Qt.IgnoreAction:
return True
if data.hasText():
ancestorL = str(data.text()).split(",")
ancestorL.reverse() #<- stored from the child up, we read from ancestor down
pIndex = QtCore.QModelIndex()
for ancestor in ancestorL:
srcRow = int(ancestor.split(";")[0])
srcCol = int(ancestor.split(";")[1])
itemIndex = self.index(srcRow, srcCol, pIndex)
pIndex = itemIndex
print itemIndex.internalPointer().get_name()
return True
来源:https://stackoverflow.com/questions/4216139/python-object-in-qmimedata