Inconsistency between model and view while updating QSqlTableModel cell
-
Background:
I have a model that stores list of users with their profile information. The users are shown on
main.qml
and their profile onprofile.qml
.I am using
ListView
( NotTableView
) to display list of users fromQSqlTableModel
and to display profile I useTableView
with the same underlying model.Problem:
When I try to update a user profile by editing their info. and hiting save button the model seems to get updated in the background but the view doesn't. To let the view show the change I have to restart my application.
Code:
main.qml:
// this qml shows list of users from model StackView { id: home anchors.fill: parent padding: 0 property var model: null initialItem: Pane { ColumnLayout { id: body ListView { id: view currentIndex: -1 height: body.height width: body.width delegate: SwipeDelegate { id: content width: parent.width height: 50 text: model.name swipe.onCompleted: { if (swipe.position > 0){ home.model = model home.pop() home.push("Profile.qml") } swipe.close() } swipe.left: Label { text: qsTr("Profile") color: "white" verticalAlignment: Label.AlignVCenter padding: 12 height: parent.height anchors.left: parent.left opacity: 2 * content.swipe.position } } model: UserModel { id: user_model } ScrollIndicator.vertical: ScrollIndicator {} } } } }
profile.qml:
// this qml shows user profile which can be edited and submitted with save button ColumnLayout { id: info spacing: 10 RowLayout { id: phone_g width: parent.width TextField { id: phone text: home.model.phone font.pixelSize: 13 } } RowLayout { id: address_g width: parent.width TextField { id: address text: home.model.address font.pixelSize: 13 } } RowLayout { id: save_l width: parent.width Button { id: save_btn text: "save" onClicked { // this is where the model is updated and both the calls to setData return true user_model.setData(user_model.index(home.model.index, 2), address.text) user_model.setData(user_model.index(home.model.index, 3), phone.text) } } } }
usermodel.cpp
// this is how I manage to show list of users with ListView from QSqlModelTable ( which usually uses TableView instead of ListView ) QVariant UserModel::data(const QModelIndex &index, int role) const{ QVariant value; if (index.isValid()) { if (role < Qt::UserRole) value = QSqlQueryModel::data(index, role); else { int columnIdx = role - Qt::UserRole - 1; QModelIndex modelIndex = this->index(index.row(), columnIdx); value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole); } } return value; } QHash<int, QByteArray> UserModel::roleNames() const{ QHash<int, QByteArray> roles; for (int i = 0; i < record().count(); i ++) { roles.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8()); } return roles; }
-
From what I understood, you are mapping custom roles in the first column to other source columns.
When you are calling setData, you call it with an index whose column is after the first one.
The base model will then calldataChanged(index, index, displayRole)
in your case.
However theListView
simply ignores those signals because it only cares about index in the first column. It has no way of knowing the data pointed by your roles in your first column have changed.What you could do is connect to the dataChanged signal in your model and do the reverse mapping, eg.: if dataChanged is called on the column 2, emit a dataChanged on column 0 with the adress role. Be carefull to reemit dataChanged only when needed to avoid infinite loops.
-
Hi Ahti,
Please have a look at https://doc.qt.io/qt-5/qabstractitemmodel.html#dataChanged
-
@GrecKo @Christian-Ehrlicher @SGaist So Do I have to override
setData
function ofQSqlTableModel
in order fordataChanged
to be implemented like this:bool UserModel::setData(const QModelIndex &index, const QVariant &value, int role){ if (index.isValid() && role == Qt::EditRole){ bool response = QSqlTableModel::setData(index, value, role); response = QSqlTableModel::submitAll(); const QModelIndex idx = this->index(index.row(), 0); emit dataChanged(idx, idx); return response; } return false; }
That still doesn't update the view. Maybe because I didn't address the custom role but i don't know how to address it exactly please help.
-
See the dataChanged documentation.
-
Did you saw the third argument of the signal ? The one you are currently not using in the code you provided. It's the one containing the custom roles.