I have a MVC QAbstractItemModel that has drag and dropped enabled, like so:
class Model(QtCore.QAbstractItemModel):
# ... other code ...
def flags(self, index):
flags = super(Model, self).flags(index)
flags |= QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled
return flags
This enables drag-and-drop but the drag-and-drop behavior is too permissive. In a tree like this:
- root
- A
- B
- C
- D
- E
I only want to allow drag-and-drop if the dropped index keeps its original parent.
In other works, I can reorder A, B, or C underneath root
and I can reorder D or E underneath C
, but I cannot move A into C
, or move D/E into A
etc.
This is a "valid" drag-and-drop:
- root
- B
- C
- D
- E
- A
because A was moved to the last row, but A still has the same parent, root
.
But this is not a valid drag-and-drop
- root
- B
- A
- C
- D
- E
A's parent was changed from root
to B
, which is not what I want.
First Attempt
I tried to achieve this "always keep the same parent" logic by subclassing QTreeView
and adding dragMoveEvent
to it, like this
class MyTreeSubClass(QtWidgets.QTreeView):
# ... more code ...
def dragMoveEvent(self, event):
super(MyTreeSubClass, self).dragMoveEvent(event)
widget = event.source()
if widget != self:
# Forbid dropping any external data to this widget
event.ignore()
return
index = widget.indexAt(event.pos())
for selected in widget.selectedIndexes():
if index.parent() != selected.parent():
event.ignore()
return
event.accept()
But this doesn't work I think because index = widget.indexAt(event.pos())
returns the same index , whether you're hovering directly over an index in the tree or between two indexes.
Is there a more reliable way to tell if a position is directly above an index (and if so, call event.reject()
)? If so, I may be able to use that to accept / reject the event.
If there's an easier way to achieve what I'm looking for, I would greatly appreciate the advice.