Calling QSortFilterProxyModel::setData properly from QML



  • Hi,

    I have a TableView with TableViewColumns with roles from a QSqlTableModel through a QSortFilterProxyModel.
    Showing the data in the table works fine, als well as sorting, filtering and deleting rows. Now I want to edit Data in the table. I will try to give the relevant code here.
    The table columns have delegates:

    Component {
        id: editorDelegate
    
        Item {
            Text {
                id: textItem
                text: styleData.value
            }
            TextField {
                id: textField
                anchors.fill: parent
                text: styleData.value
                onEditingFinished: {
                    textItem.visible = true
                    textField.visible = false
                    myModel.setModelData(model.index, textField.text, PUT_ROLE_HERE)
                }
            }
        }
    }
        [...]
    TableView {
        id: tableView
        model: myModel
    
        TableViewColumn {
            role: "date"
            title: qsTr("Date")
            width: window.width / 6 + 30
            delegate: editorDelegate
        [...]
    

    The question is, how can I put the role value (int?) to the setModelData() call?

    On the C++ side, setModelData() creates a QModelIndex and then calls setData(). Just to clarify the title. :)

    Kind regards



  • Hi,

    Hope you overridden the roleNames() and data() APIs of QSqlTableModel in your source model class. This is for handling your table columns. I have some limitations to reply your query as above overridden functions are not visible to me. Anyway I can tell you some possibilities from my experience.

    example:

    I have a table with 2 columns (ID [PK], Names) and set that table to MyModel class derived from QSqlTableModel. I have overriden these function in my class:

    QHash<int, QByteArray> MyModel::roleNames() const
    {
    ///Find out the existing role names.
    QHash<int, QByteArray> roleNameMap = QAbstractItemModel::roleNames();
    ///Fetch the DB record of specified table.
    QSqlRecord record = QSqlDatabase::database().record(this->tableName());
    ///Extract the header names and add to role name map.
    for(int columnIndex = 0; columnIndex < record.count(); ++columnIndex)
    {
    this->roleNameMap.insert(Qt::UserRole + columnIndex,
    record.fieldName(columnIndex).toLocal8Bit()); /// Qt::UserRole + 0 for "ID" column and Qt::UserRole + 1 for "Names" column
    }
    }

    QVariant MyModel::data(const QModelIndex& index, int role) const
    {
    QVariant value;
    if(role < Qt::UserRole) /// Default roles handles on base implementation.
    {
    value = QSqlTableModel::data(index, role);
    }
    else /// Our custom role handles specially here.
    {
    int columnIdx = role - Qt::UserRole;
    QModelIndex modelIndex = this->index(index.row(), columnIdx);
    value = QSqlTableModel::data(modelIndex, Qt::DisplayRole);
    }
    return value;
    }

    void MyModel::setModelData(const int id, const QVariant& newName ) /// here i am passing id instead or row index from UI, so it will work fine regardless the sort.
    {
    QModelIndex startIndex = index(0, 0); /// 0 - start from first row, 0 - index if ID column.
    QModelIndexList matchIndex = match(startIndex, Qt::UserRole + 0, id, 1, Qt::MatchExactly); /// find the desired row for update.
    if(matchIndex.count() > 0)
    {
    setData(index(matchIndex[0].row(), 1), newName); /// 1 - name column index. I used default "EditRole" here.
    submit(); /// submitAll() should call if you are using manual edit strategy.
    }
    }

    So you can call this setModelData() Q_INVOKABLE function from QML without passing role name from there.
    Hope this will help you and still have problem let me know. If you share the code of above overriden APIs in your application, it is better.

    Regards,
    Rajeesh Raveendran



  • Thank you for your answer. :)
    Maybe you could edit your post and add code markers (three backtics) to wrap your code.

    Yes, those are overridden and with the widget classes QTableView and QTreeView it works just fine. The only difference I can see in my debug out put is the role ID which is "2" (edit) from the widget classes and ... well I tried to give "2" and "259" (the role where my column is mapped to) but the result is always the same. Editing an entry from the widget classes works, from within QML it does not.

    Now I just wanted to programmatically set the role so I do not have to guess anymore.
    If I leave out the role param, EditRole is set, which kinda breaks during mapping and then edits another column in the database.

    The whole code is currently quite messy from all the trying but here it is.

    • there is a database in the backend
    • I inherited from QSqlTableModel -> SqlTableModel
    • Then I inherited from QSortFilterProxyModel -> ProxyModel

    Within SqlTableModel:

    QVariant SqlTableModel::data(const QModelIndex &index, int role) const
    {
        QVariant value;
    
        if (role < Qt::UserRole) {
            value = QSqlTableModel::data(index, role);
        } else {
            int row = index.row();
            int col = role - Qt::UserRole - 1;
    
            auto modelIndex = this->index(row, col);
    
            value = QSqlTableModel::data(modelIndex, Qt::DisplayRole);
        }
    
        return value;
    }
    
    bool SqlTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
        if (role < Qt::UserRole) {
            return QSqlTableModel::setData(index, value, role);
        } else {
            int row = index.row();
            int col = role - Qt::UserRole - 1;
    
            auto modelIndex = this->index(row, col);
    
            return QSqlTableModel::setData(modelIndex, value, Qt::DisplayRole);
        }
    }
    
    QHash<int, QByteArray> SqlTableModel::roleNames() const
    {
        QHash<int, QByteArray> roles = QSqlTableModel::roleNames();
    
        for (int i = 0; i < record().count(); ++i) {
            roles[Qt::UserRole + 1 + i] = record().fieldName(i).toLocal8Bit();
        }
    
        return roles;
    }
    

    Within the ProxyModel:

    void ProxyModel::changeData(int row, const QVariant &data, int role)
    {
        QModelIndex modelIndex = index(row, 0);
        setData(modelIndex, data, role);
    }
    
    bool ProxyModel::setData(const QModelIndex &index, const QVariant &data, int role)
    {
        QModelIndex sourceIdx = mapToSource(index);
        bool success = sourceModel()->setData(sourceIdx, data, role);
        return success;
    }
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.