Value before change?

2019-07-27 13:31发布

问题:

I have a model QStandardItemModel and subscribe to itemChanged() signal, where I get the changed item.

Is there any way how to get value of changed item before this change?

QtCore.QObject.connect(self, QtCore.SIGNAL("itemChanged(QStandardItem *)"), self.sModelChanged)  

回答1:

Reference this answer in past. (Qt, C++)

Before read my answer, I show how to implement in pyqt4. It's not my idea, But I will show how to do that.


The class QtGui.QStandardItemModel have only void itemChanged (QStandardItem*). So you have to create your custom signal itself.

1) For this case, I recommend to inheritance class QtGui.QStandardItemModel to create own class to implement get value of changed item before this change.

2) Next, override bool QStandardItemModel.setData (self, QModelIndex index, QVariant value, int role = Qt.EditRole) should be fine to track data changed (But it can't track in contractor initiated).

3) And create signal your own before set data;

Implemented example (PyQt4, Python);

import sys
from PyQt4 import QtGui, QtCore

class QNonStandardItemModel (QtGui.QStandardItemModel):
    itemBeforeAndAfterChanged = QtCore.pyqtSignal(QtCore.QModelIndex, int, QtCore.QVariant, QtCore.QVariant)
    def setData (self, indexQModelIndex, afterValueQVariant, role = QtCore.Qt.EditRole):
        beforeValueQVariant = self.data(indexQModelIndex, role)
        self.itemBeforeAndAfterChanged.emit(indexQModelIndex, role, beforeValueQVariant, afterValueQVariant)
        return QtGui.QStandardItemModel.setData(self, indexQModelIndex, afterValueQVariant, role)

myQApplication = QtGui.QApplication([])
myQTreeView = QtGui.QTreeView()

headerQStandardItemModel = QNonStandardItemModel()
def callback (indexQModelIndex, role, beforeValueQVariant, afterValueQVariant):
    print '#' * 80
    print 'Before:', beforeValueQVariant.toString()
    print 'After:', afterValueQVariant.toString()
    print 'Role:', role

headerQStandardItemModel.itemBeforeAndAfterChanged.connect(callback)
headerQStandardItemModel.setHorizontalHeaderLabels([''] * 4)
myQTreeView.setModel(headerQStandardItemModel)
row1QStandardItem = QtGui.QStandardItem('ROW 1')
row1QStandardItem.appendRow([QtGui.QStandardItem(''), QtGui.QStandardItem('COLUMN 1'), QtGui.QStandardItem('COLUMN 2'), QtGui.QStandardItem('COLUMN 3')])
headerQStandardItemModel.appendRow(row1QStandardItem)

myQTreeView.show()
sys.exit(myQApplication.exec_())


回答2:

There is no a standard way to get notified when QStandardItem is changed. However, you can easily implement it:

  • Create a new class inherited from QStandardItem.
  • Reimplement clone to create this class objects from the model.
  • Reimplement setData method and notify the item is changed. See below two ways how to do that.
  • Set new class as prototype to the model (setItemPrototype).

There are two ways to go further:

  1. Inherit you item class from QObject also to emit a signal from setData and subscribe to each item somewhere (I wouldn't do that).
  2. Add signal itemAboutToChange to QStandardItemModel.

I expand on #2:

  • Create a new class inherited from QStandardItemModel.
  • Add macro Q_OBJECT and new signal itemAboutToChange(QStandardItem*)
  • In setData of a class derived from QStandardItem call the signal from the model.

Here is how to call the signal:

 QMetaObject.invokeMethod(self.model(), "itemAboutToChange", Qt::DirectConnection, Q_ARG(QStandardItem*, self));