5

I have a map = std::map<std::string, myItemModel *>, where myItemModel inherits QAbstractItemModel.

I want now to combine all myItemModel in one single myItemModel (every other item model would be fine too). So that there is one big myItemModel.

Is there a 'qt-way' to do this?

liquid.pizza
  • 505
  • 1
  • 10
  • 26

2 Answers2

4

It can be done, but it's not trivial. It depends on your implementation of QAbstractItemModel and that's why it hasn't been done in Qt.

Here are steps to implement a model which is a collection of models:

  1. Create a new class inherited from QAbstractItemModel
  2. Add methods to add other models to that
  3. Process all signals from child models which contains indexes (you'll need to change them, look #10)
  4. Forward all signals which doesn't contain indexes.
  5. Implement rowCount and provide a sum of all models rows.
  6. Implement columnCount and provide a number of columns in your models.
  7. Implement index, return createIndex(row, column, NULL);
  8. Implement parent, return QModelIndex(); I hope your models are not trees
  9. Implement data,setData etc. addressing calls to the right model. Use methods from #10 to convert indexes.
  10. Create methods to convert a child model index to a base model index and back.
     Example (indexes):  
     BaseModel ChildModel1 ChildModel2
        0,0       0,0         
        1,0       1,0         
        2,0                   0,0
        3,0                   1,0
        4,0                   2,0

p.s. Think about creating a cache of indexes mapping.

This is an example of a method to convert a base model index to a child model index:

const QModelIndex childModelIndex(const QModelIndex& baseModelIndex) const
{
  if (!baseModelIndex.isValid())
  {
    return QModelIndex();
  }

  int count = 0;
  const int row = baseModelIndex.row();

  for (QList<QAbstractTableModel*>::const_iterator it = m_models.begin();
    it != m_models.end(); it++)
  {
    const int currentCount = (*it)->rowCount();     

    if (row >= count && row < count + currentCount)
    {       
        return (*it)->index(row - count, 0);
    }

    count += currentCount;
}

ASSERT(false);

return QModelIndex();

}

This is an example of a method to convert a child model index to a base model index:

QModelIndex baseModelIndex(const QModelIndex& childModelIndex) const
{
    int row = childModelIndex.row();

    for (QList<QAbstractTableModel*>::const_iterator it = m_models.begin();
        it != m_models.end(); it++)
    {
        if (childModelIndex.model() == *it)
        {
            return index(row, ind.column());
        }

        row += (*it)->rowCount();
    }

    return QModelIndex();
}
Ezee
  • 4,214
  • 1
  • 14
  • 29
  • How will the proposed approach solve conflicts between models. For example model1 returns "Foo" and model2 returns "Goo" from the `data()` function? – vahancho Sep 04 '14 at 10:05
  • @vahancho No any conflicts. Method `data` has an index as an argument. Suppose a view asked for the data at inx(3,0): `base->data(idx)`. See the example above. Method `data` in the base model finds appropriate model. It is model2, calculates index in this model idx2(1,0) and returns `model2->data(idx2)` – Ezee Sep 04 '14 at 10:56
3

The KDE Frameworks project contains a module called KItemModels, which includes a class called KConcatenateRowsProxyModel. It does exactly what you want. The library is released every month as part of the [KDE Frameworks releases], the code is continuously unit tested on https://build.kde.org. All this is licensed under LGPL v2 or later.

dhaumann
  • 1,590
  • 13
  • 25