I'm trying to implement drag-and-drop functionality in a QTreeView widget in PyQt6. I have enabled drag-and-drop support using setDragEnabled(True), setAcceptDrops(True), and setDropIndicatorShown(True) to show the drop indicator when dragging items.
However, after overriding the built-in drag and drop event functions (dragEnterEvent, dragMoveEvent, and dropEvent), the drop indicator (the line between rows where the dragged item can be dropped) no longer appears.
How can I restore or properly display the drop indicator between items after overriding the drag-and-drop event functions in PyQt6's QTreeView?
Here is the code I have so far:
from PyQt6 import QtCore, QtGui, QtWidgets
class ObjectTree(QtWidgets.QTreeView):
def __init__(self):
super().__init__()
self.model = QtGui.QStandardItemModel()
self.setModel(self.model)
# Enable drag and drop
self.setDragEnabled(True)
self.setAcceptDrops(True)
self.setDropIndicatorShown(True) # Show drop indicator
self._populate_tree()
def _populate_tree(self):
root_item = QtGui.QStandardItem('Root')
self.model.appendRow(root_item)
for i in range(10):
child_item = QtGui.QStandardItem(f'Child {i + 1}')
root_item.appendRow(child_item)
def dragEnterEvent(self, event):
if event.mimeData().hasFormat('application/x-item'):
event.acceptProposedAction()
else:
event.ignore()
def dragMoveEvent(self, event):
if event.mimeData().hasFormat('application/x-item'):
event.acceptProposedAction()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasFormat('application/x-item'):
data = event.mimeData().data('application/x-item')
stream = QtCore.QDataStream(data, QtCore.QIODevice.OpenModeFlag.ReadOnly)
item_name = stream.readQString()
print(f"Dropped item: {item_name}")
event.acceptProposedAction()
def startDrag(self, event):
selected_indexes = self.selectedIndexes()
if selected_indexes:
selected_item = self.model.itemFromIndex(selected_indexes[0])
mime_data = QtCore.QMimeData()
data = QtCore.QByteArray()
stream = QtCore.QDataStream(data, QtCore.QDataStream.OpenModeFlag.WriteOnly)
stream.writeQString(selected_item.text())
mime_data.setData('application/x-item', data)
drag = QtGui.QDrag(self)
drag.setMimeData(mime_data)
drag.exec(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.object_tree = ObjectTree()
self.setCentralWidget(self.object_tree)
self.setWindowTitle("PyQt6 Drag and Drop Example")
self.setGeometry(100, 100, 600, 400)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
window = MainWindow()
window.show()
app.exec()
dragMoveEvent(). Note that in a case like your is usually more appropriate to subclass the model instead and override the related methods:mimeTypes()(to add your own type),mimeData()(to write the related data when dragging) anddropMimeData(to eventually do something when dropping). The view will then conform to that automatically.