Search code examples
pyqt5pyside2qlistview

How to modify drop event in QListView


I have a QListView where items can be reordered with drag-and-drop by setting dragDropMode to DragDrop and defaultDropAction to MoveAction. How can I intercept the drop event, to find out what is trying to be moved where in relation to the rest of the list, so that I can cancel this action under certain conditions? E.g. I want to forbid moving some items behind other.


Solution

  • You can access the indexes and items involved in the dropEvent and setDropAction to Qt.IgnoreAction to cancel the drop depending on your criteria. Since you weren't specific, for this demonstration I just created one item that stays at the bottom.

    import sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    
    class List(QListView):
    
        def dropEvent(self, event):
            i = self.selectedIndexes()[0]
            j = self.indexAt(event.pos())
    
            # To access the items involved
            source = self.model().itemFromIndex(i)
            target = self.model().itemFromIndex(j)
    
            bottom = (self.model().rowCount() - 1, -1)
            if i.row() in bottom or j.row() in bottom:
                event.setDropAction(Qt.IgnoreAction)
            else:
                super().dropEvent(event)
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        window = QWidget()
        lv = List()
        lv.setDragDropMode(QAbstractItemView.DragDrop)
        lv.setDefaultDropAction(Qt.MoveAction)
        
        model = QStandardItemModel(5, 1)
        for i in range(4):
            item = QStandardItem(f'Item {i + 1}')
            item.setFlags(item.flags() & ~Qt.ItemIsDropEnabled)
            model.setItem(i, 0, item)    
        item = QStandardItem('I stay at the bottom ._.')
        model.setItem(4, 0, item)
        lv.setModel(model)
        
        vbox = QVBoxLayout(window)
        vbox.addWidget(lv)
        window.show()
        sys.exit(app.exec_())