How to associate QModelIndex with a new row?

13.9k views Asked by At

I've cooked up a QAbstractListModel whose model indexes contain a pointer I absolutely needed in order to process data. I add the data like so:

void PointListModel::addPoint(int frameNumber, QPoint const& pos)
{
    PointItem *pointItem = new PointItem( frameNumber, pos );
    QModelIndex newRow = this->createIndex( m_points.count(), 0, pointItem );

    qDebug() << newRow.internalPointer();

    beginInsertRows( newRow, m_points.count(), m_points.count() );
    m_points.insert( m_points.count( ), pointItem );
    endInsertRows();

    emit pointAdded( pointItem, pos );
}

It was only later that I realized that the argument to beginInsertRows is asking for the parent model index of the new row, not the new row's actual model index.

So, at this point in time, Qt has given me no way of supplying a QModelIndex to associate with this particular row. How do I create my own model index for this new row?

2

There are 2 answers

16
Sergei Tachenov On BEST ANSWER

Okay, I'm rewriting my answer as after some research I've found out that I got it wrong.

You shouldn't do anything special to create a new index when you add new data. You code should look like this:

PointItem *pointItem = new PointItem( frameNumber, pos );
// assume you insert a top level row, having no parent
beginInsertRows( QModelIndex(), m_points.count(), m_points.count() );
m_points.insert( m_points.count( ), pointItem );
endInsertRows();

Then you should implement the index() method which will create indexes on demand and the parent() method which will determine the parent of some index, but since you have a list model, it should probably always just return QModelIndex(). Here is a good article about creating custom models.

Here is a complete example of a working QAbstractListModel:

class MyModel: public QAbstractListModel {
  Q_OBJECT
  public:
    virtual QModelIndex index(int row, int column = 0,
        const QModelIndex &parent = QModelIndex()) const;
    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
    virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    void add(int i);
  private:
    QList<int> list;
};

void MyModel::add(int i)
{
  beginInsertRows(QModelIndex(), list.size(), list.size());
  list.append(i);
  endInsertRows();
}

QModelIndex MyModel::index(int row, int column,
        const QModelIndex &parent) const
{
  return hasIndex(row, column, parent) ? createIndex(row, column, (void*)&list[row])
    : QModelIndex();
}

int MyModel::rowCount(const QModelIndex &parent) const
{
  if (parent.isValid())
    return 0;
  return list.size();
}

QVariant MyModel::data(const QModelIndex &index,
    int role) const
{
  if (!index.isValid())
    return QVariant();
  if (role != Qt::DisplayRole)
    return QVariant();
  return QVariant(QString::number(*static_cast<int*>(index.internalPointer())));
}
0
Marc Mutz - mmutz On

I've cooked up a QAbstractListModel whose model indexes contain a pointer I absolutely needed in order to process data.

If you start with wrong requirements, you end up with wrong solutions :)

A list model is simple enough so that you don't need more than the QModelIndex's row() to uniquely define the data the index addresses.

So, given a QModelIndex mi, when you before did

PointItem * item = static_cast<PointItem*>(mi.internalPointer());

you can instead do

PointItem * item = plm->pointItemFromIndex(mi);

where plm is your PointListModel. If you don't have a pointer to it lying around when you need to access the PointItem, you can reconstruct it like this:

PointItemModel * plm = qobject_cast<PointItemModel*>(mi.model());
// check for !plm here (!mi.isValid() || qobject_cast fails)

In turn, PointListMode::pointItemFromIndex() would do the actual work:

PointItem * PointListMode::pointItemFromindex(const QModelIndex &mi) const {
    return mi.isValid() ? m_points[mi.row()] : 0 ;
}

This is the most important thing to realize when working with QAbstractListModel in Qt: Mentally replace QModelIndex with int row, ignore everything else it has (an invalid QModelIndex has row() == -1).

Same for QAbstractTableModel: mentally reduce the QModelIndex to int row, int column. Forget everything else.

The only time you need the full QModelIndex (including its internalPointer() or internalId() is when you implement a tree model (QAbstractItemModel).