How to get old value when processing signal QAbstractItemModel :: dataChanged ()?

I have a QTableView that installed QStandardItemModel . The user edits the data at some index in the view, and then the model emits the signal dataChanged() . In SLOT, where I process SIGNAL, the QModelIndex user range changes, and so I can get the new values ​​that the user entered. How can I get the old values ​​at this point?

+6
source share
4 answers

Since QStandardItemModel is a simple model, it has no signal with this function. If you want such a function, you can subclass QAbstractItemModel and have your own custom class and implement setData and emit a custom signal that contains both old and new values.

As a workaround, you can connect the itemChanged QStandardItemModel signal to some slot:

 connect(model,SIGNAL(itemChanged(QStandardItem*)),this, SLOT(onModelChanged(QStandardItem*))); 

And save the new value as Qt::UserRole in the model to use it as the old value the next time the slot is called:

 void MyClass::onModelChanged(QStandardItem *item) { disconnect(model,SIGNAL(itemChanged(QStandardItem*)),this, SLOT(onModelChanged(QStandardItem*))); QVariant oldValue = item->data(Qt::UserRole); item->setData(item->data(Qt::DisplayRole), Qt::UserRole); //Store the new value for next use connect(model,SIGNAL(itemChanged(QStandardItem*)),this, SLOT(onModelChanged(QStandardItem*))); } 
+2
source

After some research, I realized that there is no standard way to achieve this behavior. To solve the problem, I had to inherit QStandardItemModel and override setData() as follows:

 class RecallModel : public QStandardItemModel { public: RecallModel (QObject * parent = 0) : QStandardItemModel(parent) {} // Reimplemented bool setData(const QModelIndex &index, const QVariant &value, int role= Qt::EditRole) { // backup the previous model data if (role == Qt::EditRole || role == Qt::DisplayRole) QStandardItemModel::setData(index, data(index), Qt::UserRole + 1); return QStandardItemModel::setData(index, value, role); } }; 

And after that, I can access the old data in the slot by processing the dataChanged() signal:

 void SomeObject::handleDataChange(const QModelIndex &topLeft, const QModelIndex &bottomRight) { ... const QVariant &vOldData = index.data(Qt::UserRole + 1); // here is the old data const QVariant &vNewData = index.data(Qt::DisplayRole); // here is the new data ... } 
+2
source

The user can modify the data using the delegate, so it’s possible:

 #ifndef ITEMDELEGATE_H #define ITEMDELEGATE_H #include <QItemDelegate> class ItemDelegate : public QItemDelegate { Q_OBJECT public: explicit ItemDelegate(QObject *parent = 0); protected: QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget * editor, const QModelIndex & index) const; void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const; void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const; signals: void dataChanged(QString oldValue,QString newValue) const; public slots: private: mutable QString old;//we want change member data in const method }; #endif // ITEMDELEGATE_H 

As you can see, many const methods are default, so I did some tricks (e.g. mutable ) to avoid problems. Also in my edited answer I do not store the old data in UserRole+1 , everything is done with the old mutable variable.

castes:

 #include "itemdelegate.h" #include <QLineEdit> #include <QDebug> ItemDelegate::ItemDelegate(QObject *parent) : QItemDelegate(parent) { } QWidget *ItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QLineEdit *editor = new QLineEdit(parent); return editor; } void ItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { old = index.model()->data(index, Qt::EditRole).toString();//store old data QLineEdit *line = qobject_cast<QLineEdit*>(editor); line->setText(old); } void ItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index)const { QLineEdit *line = static_cast<QLineEdit*>(editor); QString data = line->text(); emit dataChanged(old, line->text()); model->setData(index, data); } void ItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { editor->setGeometry(option.rect); } 

Using:

  ItemDelegate * del = new ItemDelegate; connect(del,&ItemDelegate::dataChanged,[=](QString oldValue,QString newValue) { qDebug() << "old" << oldValue<< "new" <<newValue ; }); ui->tableView->setItemDelegate(del); 

I tested it and it works. He will work with different models. QTableView is used by default lineEdit as a delegate, so the user does not see any changes in the view.

I used C++11 ( CONFIG += c++11 to .pro file) and the new syntax for signals and slots , but of course you can use the old syntax if you want.

+1
source

All decisions before this answer are based on Qt-specific functions. But you can move the functions INSERT, UPDATE, DELETE (not SQL, but general) outside of Qt. Take a look at the design template for the work unit and the corresponding domain object template from "Enterprise Application Architecture Templates" .

You can change these templates to store old values ​​and get them when changing during setData() .

0
source

Source: https://habr.com/ru/post/978941/


All Articles