Updating QSortFilterProxyModel child items after adding rows to a tree model



  • Hi guys! Could you please help me with the following problem.

    Brief:
    @After adding a row to the source model, when called invalidateFilter()->filterAcceptsRow() of the proxy model it receives the outdated indices from the source model (those which were created before new row is added), as well as correct indices. Looks like proxy model remembers sources model's structure and updates it only partially@

    I have the tree model derived from the QAbstractItemModel.
    The 3-level model reflects some hierarchical data stored in memory. I use the 32-bit internalId field to store all the tree structure information as follows:
    [-- L0 pos (15bit) --][-- L1 pos (15bit)--][-- level (2bit)--]
    @
    0
    1
    2--0--x
    |__1--y@ So for "y" node L0 = 2, L1 = 1 and level = 2, for "2" node L0 = 2, L1 = 0(any), level = 0

    Since the data in memory preserve its order and every item knows about its position in the tree so it lets to implement index() and parent() and data() functions and it works fine until a filtering model comes to the scene.

    Let's start with the following structure (B has children "0" and "1", "0" has no children) (state BEFORE):
    @B--0
    |__1--y@

    Then the data is added on the 0-th position. The beginInsertRows() is called A the the item is added and endInsertRows() is called inside the model. Then beginInsertRows() is called for A-0 and this item(s) is added and then endInsertRows(). (state AFTER)
    @A--0--z
    B--0
    |__1--y@

    Then, the dataChanged() is emitted from the source model since A (probably) changes its state which causes the QSortFilterProxyModel::invalidateFilter() to be called (or it can be called in other way). Inside invalidateFilter() the
    filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent)
    is called with different source parent indices.

    The disaster comes from the last ones: we receive sourceParent's as B-1 (with row == 0 for B-1-y), B (with row == 1 for B-1) etc. AND we receive the sourceParent A-1 (with row ==0, pointing to A-1-y). Obviously, the latter somehow remains (inside the QSortFilterProxyModel ?!) from state BEFORE (interanlId value as for B-1-y) and it causes crash when trying to get the child index as follows:
    sourceModel()->index(sourceRow, 0, sourceParent), since there are already no node "1" under A in the memory I'm trying to display.

    So,

    1. How to correctly update filter model to make it know that the child structure is changed in the source model and avoid receivind an outdated indices into filterAcceptsRow()?
    2. Is this idea of building tree is sane (while searching for this problem solurion I've found exactly the same proposal http://lynxline.com/designing-tree-like-models/)

    Filtering model code:
    @TractsListProxyModel::TractsListProxyModel(QObject *parent)
    : QSortFilterProxyModel(parent),
    m_state(0)
    {
    setDynamicSortFilter(true);
    }

    bool TractsListProxyModel::filterAcceptsRow(int sourceRow,
    const QModelIndex &sourceParent) const
    {

    QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
    
    Tract::Ptr tract = index.data(Qt::EditRole).value<Tract::Ptr>();
    
    return stateFilter(tract) && QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
    

    }

    bool TractsListProxyModel::stateFilter(Tract::Ptr tract) const
    {
    return m_state == 0 ?
    true
    : tract->getState() == m_state;
    }

    void TractsListProxyModel::setStateFilter(terminal::TractState::Type state)
    {
    m_state = state;

    invalidateFilter();
    

    }

    void TractsListProxyModel::setSourceModel(QAbstractItemModel *model)
    {
    connect_assert(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
    this, SLOT(sourceModelDataChanged(QModelIndex, QModelIndex)));

    QSortFilterProxyModel::setSourceModel(model);
    

    }

    void TractsListProxyModel::sourceModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
    {
    Q_UNUSED(topLeft);
    Q_UNUSED(bottomRight);
    invalidateFilter();
    }@

    P.S. I've tried to connect
    @ connect(sourceModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
    this, SLOT(invalidate()));
    connect(sourceModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
    this, SLOT(invalidate()));
    connect(sourceModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
    this, SLOT(invalidate()));
    connect(sourceModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
    this, SLOT(invalidate()));
    connect(sourceModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
    this, SLOT(invalidate()));
    connect(sourceModel, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
    this, SLOT(invalidate()));
    connect(sourceModel, SIGNAL(modelReset()),
    this, SLOT(invalidate()));
    @
    But with no avail


Log in to reply
 

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