Qt World Summit: Submit your Presentation

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 on profile.qml.

    I am using ListView ( Not TableView ) to display list of users from QSqlTableModel and to display profile I use TableView with the same underlying model.


    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.



     // 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
                                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 {}


        // 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)


    // 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;

  • Qt Champions 2018

    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 call dataChanged(index, index, displayRole) in your case.
    However the ListView 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.

  • This post is deleted!

  • This post is deleted!

  • @GrecKo @Christian-Ehrlicher @SGaist So Do I have to override setData function of QSqlTableModel in order for dataChanged 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.

  • Lifetime Qt Champion

  • @SGaist I already tried the documentation but I still don't understand please help

  • Lifetime Qt Champion

    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.