Drag and drop from one QListView to another QListView

Viewed 109

I would like to implement the drag and drop feature from one QListView to another. The drag and drop action would copy the text of the dragged item to the other QListView. I want the drag and drop to be unidirectional, I mean only from QListView A to QListView B but not the contrary. To do so, I derived two classes from QListView, one for the draggable view and one for the droppable one. Unfortunately, this fails, I am prevented from dragging and dropping my item. Here is the code I could derive so far:

import sys

from PyQt5.QtWidgets import QListView, QApplication

from PyQt5.QtGui import QStandardItemModel, QStandardItem

from PyQt5 import QtCore


class DraggableListView(QListView):

    def __init__(self, parent):
        super().__init__(parent)

        self.setDragEnabled(True)

    def flags(self, index):

        flags = super(DraggableListView, self).flags(index)

        if index.isValid():
            return flags | QtCore.Qt.ItemIsDragEnabled


class DroppableListView(QListView):

    def __init__(self, parent):
        super().__init__(parent)

        self.setDefaultDropAction(QtCore.Qt.CopyAction)
        self.setAcceptDrops(True)
        self.setDropIndicatorShown(True)

    def dragMovEvent(self, e):
        e.accept()

    def dragEnterEvent(self, e):

        if e.mimeData().hasFormat('text/plain'):
            e.accept()
        else:
            e.ignore()

    def dropEvent(self, e):

        item = QStandardItem(e.mimeData().text())
        self.model().appendRow(item)


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        draggable_list_view = DraggableListView(self)
        draggable_list_view.setDragEnabled(True)
        draggable_list_view.move(30, 65)
        model1 = QStandardItemModel()
        draggable_list_view.setModel(model1)
        for it in ['yo', 'yi', 'ya']:
            item = QStandardItem(it)
            model1.appendRow(item)

        droppable_list_view = DroppableListView(self)
        droppable_list_view.move(190, 65)
        model2 = QStandardItemModel()
        droppable_list_view.setModel(model2)

        self.setGeometry(300, 300, 300, 150)


def main():

    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    app.exec_()


if __name__ == '__main__':
    main()

Would you have any idea/hint to make it work ?

1 Answers

it is not necessary to override any method of QListView since it is only necessary to enable the flags:

import sys

from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QListView, QApplication, QWidget, QHBoxLayout


class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        draggable_list_view = QListView()
        draggable_list_view.setDragEnabled(True)
        model1 = QStandardItemModel()
        draggable_list_view.setModel(model1)
        for it in ["yo", "yi", "ya"]:
            item = QStandardItem(it)
            model1.appendRow(item)

        droppable_list_view = QListView()
        droppable_list_view.setAcceptDrops(True)
        droppable_list_view.setDropIndicatorShown(True)
        model2 = QStandardItemModel()
        droppable_list_view.setModel(model2)

        lay = QHBoxLayout(self)
        lay.addWidget(draggable_list_view)
        lay.addWidget(droppable_list_view)

        self.setGeometry(300, 300, 300, 150)


def main():

    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    app.exec_()


if __name__ == "__main__":
    main()

Note: When dragging an item from any model the mimetype "text/plain" is not used but a custom mimetype "application/x-qabstractitemmodeldatalist".

Related