Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

I don't understand QAbstractTableModel



  • While I have read the docs and tried to understand the Model-View architecture of QTableView UI component, I'm still unable to well bind data to table model.

    I used this class prototype for the implementation of QAbstractTableModel:

    class TableModel : public QAbstractTableModel
    {
    public:
        TableModel(QObject *parent = nullptr);
        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
        int columnCount(const QModelIndex &parent = QModelIndex()) const override;
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
        QVariant headerData(int section, Qt::Orientation orientation,
                                        int role) const;
    

    the model has already an instance which is set for the table view. The data binding is implemented within the data() method:

    static std::vector<Task> tasks = {};
    
    QVariant TableModel::data(const QModelIndex &index, int role) const
    {
        int task_index = index.row();
        TaskProperty task_property = static_cast<TaskProperty>(index.column());
        Task task = tasks.at(static_cast<unsigned long>(task_index));
        QString cellValue = "";
        switch (task_property)
        {
        case NAME:
            cellValue = QString::fromStdString(task.name);
            break;
        default:
            break;
        }
        if (role == Qt::DisplayRole)
           return cellValue;
    
        return QVariant();
    }
    

    As described above, the data that has to be binded to the view, is within the vector tasks. But after this shared vector gets updated with newer values from an external class, nothing gets loaded within the view.

    I really got stuck at this point, and I'm unable to understand how data binding works for the model QAbstractTableModel and its view QTableView.

    Can anyone try to explain how this works please?


  • Moderators

    @kaisbe said in I don't understand QAbstractTableModel:

    As described above, the data that has to be binded to the view, is within the vector tasks. But after this shared vector gets updated with newer values from an external class, nothing gets loaded within the view.

    You need to emit dataChanged() from model if data has changed. If you added or removed some items, call beginInsertRows(), endInsertRows() etc. Without that the view does not know that anything has changed, so it won't update itself.



  • @sierdzio said in I don't understand QAbstractTableModel:

    dataqChanged()

    Thanks for the quick response.

    OK, I understood, but could you provide me with a documentation link that talks about the need of emitting dataChanged() and calling beginInsertRows() + endInsertRows()?





  • @VRonin

    • Concerning the first link:

    The dataChanged() and headerDataChanged() signals must be emitted explicitly when reimplementing the setData() and setHeaderData() functions, respectively.

    If I understand correctly, I need to implement the setData() method, that is defined as:

    Sets the role data for the item at index to value.

    But, I don't understand what the role is - frankly I don't understand anything concerning setData() and how to emit dataChanged().

    • Concerning the second link:

    The model first calls the beginInsertRows() function to inform other components that the number of rows is about to change. The function specifies the row numbers of the first and last new rows to be inserted, and the model index for their parent item. After changing the string list, it calls endInsertRows() to complete the operation and inform other components that the dimensions of the model have changed, returning true to indicate success.

    Do I need to override the insertRows() method or just:

    1. call tablemodel->beginInsertRows()
    2. perform the push back of my static vector variable at this time
    3. call tablemodel->endInsertRows()


  • Depends which rout you are going down:

    • the model is the data
    • the model is an interface on top of the data

    My feeling is that you are in the second case i.e. you want a way to feed a view with data coming from somewhere else (static std::vector<Task> tasks)

    In this case whenever a Task in the vector is changed/inserted/removed from somewhere outside of the model you also have to communicate to the model that change happened. This is usually done via a signal connected to a slot in the model. This slot will then take care of finding out the QModelIndex that corresponds to the changed item and emit dataChanged.

    Chapter 3 of this book is excellent to explain how models work


Log in to reply