Search code examples
pythondrag-and-droppyqtqtreeview

QTreeView with drag and drop support in PyQt


In PyQt 4 I would like to create a QTreeView with possibility to reorganize its structure with drag and drop manipulation.

I have implemented my own model(QAbstractItemModel) for QTreeView so my QTreeView properly displays the data. Now I would like to add drag and drop support for tree's nodes to be able to move a node inside the tree from one parent to another one, drag-copy and so on, but I cannot find any complete tutorial how to achieve this. I have found few tutorials and hints for QTreeWidget, but not for QTreeView with custom model. Can someone point me where to look?

Thank you.


Solution

  • You can enable drag and drop support for tree view items by setting QtGui.QAbstractItemView.InternalMove into the dragDropMode property of the treeview control. Also take a look at the documentation here Using drag & drop with item views. Below is a small example of a treeview with internal drag and drop enabled for its items.

    import sys
    from PyQt4 import QtGui, QtCore
    
    class MainForm(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(MainForm, self).__init__(parent)
    
            self.model = QtGui.QStandardItemModel()
    
            for k in range(0, 4):
                parentItem = self.model.invisibleRootItem()
                for i in range(0, 4):
                    item = QtGui.QStandardItem(QtCore.QString("item %0 %1").arg(k).arg(i))
                    parentItem.appendRow(item)
                    parentItem = item
    
            self.view = QtGui.QTreeView()
            self.view.setModel(self.model)
            self.view.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
    
            self.setCentralWidget(self.view)
    
    def main():
        app = QtGui.QApplication(sys.argv)
        form = MainForm()
        form.show()
        app.exec_()
    
    if __name__ == '__main__':
        main()
    

    Edit0: treeview + abstract model with drag and drop support

    import sys
    from PyQt4 import QtGui, QtCore
    
    class TreeModel(QtCore.QAbstractItemModel):
        def __init__(self):
            QtCore.QAbstractItemModel.__init__(self)
            self.nodes = ['node0', 'node1', 'node2']
    
        def index(self, row, column, parent):
            return self.createIndex(row, column, self.nodes[row])
    
        def parent(self, index):
            return QtCore.QModelIndex()
    
        def rowCount(self, index):
            if index.internalPointer() in self.nodes:
                return 0
            return len(self.nodes)
    
        def columnCount(self, index):
            return 1
    
        def data(self, index, role):
            if role == 0: 
                return index.internalPointer()
            else:
                return None
    
        def supportedDropActions(self): 
            return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction         
    
        def flags(self, index):
            if not index.isValid():
                return QtCore.Qt.ItemIsEnabled
            return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | \
                   QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled        
    
        def mimeTypes(self):
            return ['text/xml']
    
        def mimeData(self, indexes):
            mimedata = QtCore.QMimeData()
            mimedata.setData('text/xml', 'mimeData')
            return mimedata
    
        def dropMimeData(self, data, action, row, column, parent):
            print 'dropMimeData %s %s %s %s' % (data.data('text/xml'), action, row, parent) 
            return True
    
    
    class MainForm(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(MainForm, self).__init__(parent)
    
            self.treeModel = TreeModel()
    
            self.view = QtGui.QTreeView()
            self.view.setModel(self.treeModel)
            self.view.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
    
            self.setCentralWidget(self.view)
    
    def main():
        app = QtGui.QApplication(sys.argv)
        form = MainForm()
        form.show()
        app.exec_()
    
    if __name__ == '__main__':
        main()
    

    hope this helps, regards