How to set data inside a QAbstractTableModel

29,592

Solution 1

How the actual data is kept in memory, generated or queried from a data store is completely up to you. If it's static data, you can use the Qt container classes or custom data structures.

You only need to reimplement the setData() method for editable models.

There are 4 methods you need to implement in a non-editable QAbstractTableModel subclass:

  • int rowCount()
  • int columnCount()
  • QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole )
  • QVariant data(const QModelIndex & index, int role = Qt::DisplayRole)

These methods are called from the view, usually a QTableView instance. The first two methods should return the dimensions of the table. For example, if rowCount() returns 10 and columnCount() returns 4, the view will invoke the data() method 40 times (once for each cell) asking for the actual data in your model's internal data structures.

As an example suppose you have implemented a custom slot retrieveDataFromMarsCuriosity() in your model. This slot populates a data structure and is connected to a QPushButton instance, so you get fresh data by clicking a button. Now, you need to let the view know when the data is being changed so it can update properly. That's why you need to emit the beginRemoveRows(), endRemoveRows(), beginInsertRows(), endInsertRows() and its column counterparts.

The Qt Documentation has everything you need to know about this.

Solution 2

You don't need to use setData(...). Instead, you need to subclass QAbstractTableModel in such a way that its methods rowCount(), columnCount(), data(index) and potentially headerData(section, horizontalOrVertical) return the data you wish to display. Here's an example based on PyQt5:

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

headers = ["Scientist name", "Birthdate", "Contribution"]
rows =    [("Newton", "1643-01-04", "Classical mechanics"),
           ("Einstein", "1879-03-14", "Relativity"),
           ("Darwin", "1809-02-12", "Evolution")]

class TableModel(QAbstractTableModel):
    def rowCount(self, parent):
        # How many rows are there?
        return len(rows)
    def columnCount(self, parent):
        # How many columns?
        return len(headers)
    def data(self, index, role):
        if role != Qt.DisplayRole:
            return QVariant()
        # What's the value of the cell at the given index?
        return rows[index.row()][index.column()]
    def headerData(self, section, orientation, role):
        if role != Qt.DisplayRole or orientation != Qt.Horizontal:
            return QVariant()
        # What's the header for the given column?
        return headers[section]

app = QApplication([])
model = TableModel()
view = QTableView()
view.setModel(model)
view.show()
app.exec_()

It's taken from this GitHub repository and displays the following table:

QAbstractTableModel example

Share:
29,592
user1173240
Author by

user1173240

Updated on July 09, 2022

Comments

  • user1173240
    user1173240 almost 2 years

    I need to implement a table with Qt.

    I believe I'll be suing a QAbstractTableModel, with a QTableView using this model.

    I understand I'll have to edit the rowCount(), columnCount(), and data() functions of the model.

    However, I don't understand how to exactly set the data inside the model, so that data() function can retrieve it..

    Is the setData() function provided for this purpose? I have seen it takes EditRole as its parameter, which I don't want, as I don't want my table to be editable.

    So, how do I "set" data inside the model, or have data for the model to get at, using data() function?

    Also, how is the data() function called, i.e., who calls it and where would it need to be called?

    Please help me with this.

    Thanks.

  • user1173240
    user1173240 over 11 years
    Thanks a lot for your prompt help. I'll certainly look into it.
  • Cool_Coder
    Cool_Coder over 10 years
    @dschulz, is it absolutely necessary to use beginInsertRows(), etc for the first initialisation? Then I dont understand the purpose of dataChanged(). Why cannot Qt retrieve the information only using rowCount(), columnCount() & data()? Why is there a need to create rows & columns using beginInsertRows(), etc?
  • dschulz
    dschulz over 10 years
    @Cool_Coder yes, it is necessary if you reimplement insertRows(). You must call beginInsertRows() before adding items to your model's data container (be it a QList or whatever container class you use). Note that "data changed" doesn't necessarily implies that rows or columns are being added or removed. dataChanged() means that data in existing items in the model was updated, so the view can retrieve that updated data. Just think about what happens when you correct your name in a editable QTableView cell.
  • dschulz
    dschulz over 10 years
    But wait, I don't think it is strictly necessary to call beginInsertRows() and endInsertRows() when inserting items in the model's datastore during the initialisation of the model, specially if you haven't attached the model to a view yet.
  • Cool_Coder
    Cool_Coder over 10 years
    Working on it for 2 days & finally got it working! Looks like if we dont use begin...() & end...() the model does not create internal index's. Hence I was getting invalid model index's. And thanks for the info regarding dataChanged(). Untill now I found Qt's help very good but somehow I feel they could a lot better for model/view architecture. It gets confusing to a new programmer...
  • user1173240
    user1173240 over 10 years
    dataChanged() works for any kind of dtaa change withinthe existing model, i.e. both a possible change in the number of rows and columns, and a change within the existing ones? I had this issue, where new rows were being inserted, but the` dataChanged()` signal was perhaps not conveying the same, as no new rows were being inserted in my view, only the existing data within the rows, if changed, would change in the view.