PyQt 5 QStandardItem Storing Old Parent

1k views Asked by At

Using PyQt 5 I have been trying to find a way to get the old parent from a drag and drop operation on an item in a QTreeView situation using QStandardItemModel and QStandarItems.

I really want to avoid creating my own model if possible.

My current attempt is to store the current parent in the item when it is created as the "old parent" and it should not be updated in the move so I can reference it update the values in the old parent items and then update the "old parent" in the moved item to the new current parent.

I can not seem to get it to work though, here is the code I am trying to use to store the "old parent" when the item is created:

item.setData(parent.index(),(Qt.UserRole+3))

When I run this however I get the following error:

QVariant::save: unable to save type 'QModelIndex' (type id: 42).

and I can not reference the old parent at this point...

I found one reference using c++ and a lot of "pointer casting" but I could not figure out how to convert the code to Python and PyQt 5.

C++ Reference: https://forum.qt.io/topic/1690/qvariant-save-load-unable-to-save-type-x/19

Thanks for any help!

1

There are 1 answers

1
ekhumoro On BEST ANSWER

The model has some signals that fire whenever an item's children are inserted or removed, so these could be used to automatically update the item.

After some experimentation, I found that the signals need to be used with a queued connection, so that the model has a chance to fully update:

model.rowsInserted.connect(slot, type=QtCore.Qt.QueuedConnection)
model.rowsRemoved.connect(slot, type=QtCore.Qt.QueuedConnection)

But other than that, the implementation is quite simple. There is no need to store any extra information in the items, as the updates can be performed dynamically.

Here is a basic demo script:

from PyQt5 import QtCore, QtGui, QtWidgets

class Window(QtWidgets.QTreeView):
    def __init__(self):
        super(Window, self).__init__()
        self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
        self.setDragDropOverwriteMode(False)
        self.header().hide()
        model = QtGui.QStandardItemModel(self)
        model.rowsInserted.connect(
            self.sumItems, type=QtCore.Qt.QueuedConnection)
        model.rowsRemoved.connect(
            self.sumItems, type=QtCore.Qt.QueuedConnection)
        self.setModel(model)
        parent = model.invisibleRootItem()
        for index in range(3):
            item = QtGui.QStandardItem('0')
            parent.appendRow(item)
            for row in range(1, 5):
                child = QtGui.QStandardItem(str(row))
                item.appendRow(child)
        self.expandAll()

    def sumItems(self, index, first, last):
        if index.isValid():
            total = 0
            parent = self.model().itemFromIndex(index)
            for row in range(parent.rowCount()):
                child = parent.child(row)
                if child is not None:
                    total += int(child.text())
            parent.setText(str(total))

if __name__ == '__main__':

    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(700, 100, 250, 300)
    window.show()
    sys.exit(app.exec_())