QSortFilterProxyModel: inconsistent changes reported by source model



  • Good day!

    There is a table model of the heir QAbstractTableModel. It displays on a QTableView via QSortFilterProxyModel.
    All works fine and is even deleted, but the process in creators (in debug mode) is displayed - QSortFilterProxyModel: inconsistent changes reported by source model.
    And removal about 1000 lines ccontinue for about 1 minute - it is long, i think , I suppose maybe because of this error.

    Prompt where to look?



  • You'll need to post some code in order for people to be able to help you with this. Can you make a small compilable example that reproduces the problem please?



  • I think the message is a simple problem.

    Excerpt from the code:

    catalogTableModel.h
    @
    class CatalogTableModel : public QAbstractTableModel
    {
    Q_OBJECT
    public:
    explicit CatalogTableModel(TlsPrices *lsItem, QObject *parent);
    ~CatalogTableModel ();

    int                   rowCount         (const QModelIndex &parent = QModelIndex ()) const;
    int                   columnCount   (const QModelIndex &parent = QModelIndex ()) const;
    QVariant         headerData     (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
    Qt::ItemFlags flags                (const QModelIndex &index) const;
    QVariant         data                  (const QModelIndex &index, int role = Qt::DisplayRole) const;
    bool                 hasChildren    (const QModelIndex &parent = QModelIndex ()) const;
    bool                 setData           (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    bool                 removeRows (int row, int count, const QModelIndex &parent = QModelIndex ());
    bool                removeRowsWithCondition(int column, QString value);
    bool                insertRows     (int row, int count, const QModelIndex &parent = QModelIndex ());
    TlsPrices*      getPointerToData () {   return listItem;}
    void                setHeaderList (QList<QString> ls) { keys = ls;}
    

    private:
    TlsPrices *listItem;
    QList <QString> lsNameAttr;
    QList <QString> keys; //list with names of columns
    };
    @



  • catalogTableModel.cpp

    @
    #include <QSize>
    #include <QFont>
    #include <QFontMetrics>
    #include "catalogtablemodel.h"

    CatalogTableModel::CatalogTableModel(TlsPrices *lsItem, QObject *parent) :
    QAbstractTableModel(parent)
    {
    listItem = lsItem;
    }

    CatalogTableModel::~CatalogTableModel()
    {

    }

    int CatalogTableModel::rowCount(const QModelIndex &parent) const
    {
    if (parent.isValid())
    return 0;
    return listItem->count() ;
    }

    int CatalogTableModel::columnCount(const QModelIndex &parent) const
    {
    return keys.count();//7;//lsNameAttr.count() ;
    }

    QVariant CatalogTableModel::headerData(int section, Qt::Orientation orientation, int role) const
    {
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
    {
    return keys.at(section);
    }
    return QAbstractItemModel::headerData(section, orientation, role);
    }

    Qt::ItemFlags CatalogTableModel::flags(const QModelIndex &index) const
    {
    bool isSpec = false;
    if (keys.count() < 6)
    isSpec = true;
    if (index.column() == 6 || (index.column() == 2 && isSpec))
    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;

    return QAbstractItemModel::flags(index);
    

    }

    QVariant CatalogTableModel::data(const QModelIndex &index, int role) const
    {
    if (!index.isValid() || !listItem->count())
    return QVariant ();
    if (role == Qt::FontRole)
    {
    return QFont ("Helvetica");
    }
    if (role == Qt::SizeHintRole)
    {
    //deside best size
    QFont f ("Helvetica");
    QFontMetrics fm (f);
    if (index.column() == 0 || index.column() == 1
    || index.column() == 3)
    {
    int w = fm.width (listItem->at(index.row())->getData(index.column()).toString());
    int h = fm.height() - 2;
    if ( (w/200) > 1)
    {
    double ost = w/double (200);
    h = h*ost; //(w/200);
    w = 200;
    }
    return QSize (w, h);
    }
    return QSize (50, 25);
    //QFont
    //listItem->at(index.row())->getData(index.column()).toString().;
    }
    // QList <QString> keys = listItem->keys();
    if (role == Qt::DisplayRole || role == Qt::EditRole)
    {
    bool isSpec = false;
    if (keys.count() < 6)
    {
    isSpec = true;
    if (index.column() == 4)
    {
    double summa = listItem->at(index.row())->getData(2, isSpec).toDouble()*listItem->at(index.row())->getData(3, isSpec).toDouble();
    return summa;
    }
    }
    return listItem->at(index.row())->getData(index.column(), isSpec);

    }
    else if ( role == Qt::UserRole)
    {
    
    }
    return QVariant ();
    

    }

    bool CatalogTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
    if (index.column() == 6)
    {
    listItem->at(index.row())->order = value.toInt();

        return true;
     }
    

    // bool isSpec = false;
    if (keys.count() < 6)
    {
    //isSpec = true;
    if (index.column() == 2)
    {
    listItem->at(index.row())->price = value.toDouble();
    reset ();//for updating views
    return true;
    }

    }
     return false;
    

    }

    bool CatalogTableModel::insertRows (int row, int count, const QModelIndex &parent)
    {
    return true;
    }

    bool CatalogTableModel::removeRows (int row, int count, const QModelIndex &parent)
    {
    //TODO:
    Q_UNUSED (parent);
    beginRemoveRows(QModelIndex(), row, row + count - 1);
    //int column = row + count;
    for (int i = 0; i < count; ++i)
    {
    delete listItem->at(row);
    listItem->removeAt(row);
    }
    emit layoutChanged();
    endRemoveRows();
    return true;
    }

    bool CatalogTableModel::hasChildren(const QModelIndex &parent) const
    {
    return !parent.isValid();
    }

    bool CatalogTableModel::removeRowsWithCondition(int column, QString value)
    {
    //int i = 0;
    int sizeOfList = listItem->count()-1;
    //while (i < sizeOfList)
    for (int i = sizeOfList; i >= 0; --i)
    {
    if (listItem->at(i)->getData(column).toString() == value)
    {
    removeRows(i, 1);
    }
    }
    }

    @

    section of mainwindows.cpp

    @
    void MainWindow::setHierarhy()
    {
    if (ui->checkBox->isChecked())
    {
    tableProxyModel->setSourceModel(model.getTableModel());//вот с этой моделью работаю
    ui->treeView->setVisible(false);
    }
    else
    {
    tableProxyModel->setSourceModel(&model);
    ui->treeView->setVisible(true);
    }
    }

    void MainWindow::delRowPrice()
    {
    //model.getTableModel()->removeRows( tableProxyModel->mapToSource(ui->tableView->currentIndex()).row(), 1);
    int row = ui->tableView->currentIndex().row();
    if (ui->tableView->currentIndex().isValid())
    tableProxyModel->removeRows(ui->tableView->currentIndex().row(), 1);
    //tableProxyModel->removeRow(ui->tableView->currentIndex().row());
    ui->tableView->setCurrentIndex(tableProxyModel->index(row, 0));
    }

    @



  • I find this:

    @int proxy_count = proxy_to_source.size();
    if (proxy_count > source_to_proxy.size()) {
    // mapping is in an inconsistent state -- redo the whole mapping
    qWarning("QSortFilterProxyModel: inconsistent changes reported by source model");
    remove_from_mapping(source_parent);
    Q_Q(QSortFilterProxyModel);
    q->reset();
    return;
    }@
    but i don't know how right set size of proxy model.
    unless of course this is the case.



  • While using the following decision:
    @
    void MainWindow::cancelLoadPrice ()
    {
    tableProxyModel->setSourceModel( NULL ); //reset proxy model
    for (int i = 0; i < lsProperty.count(); ++i)
    {
    if (lsProperty.at(i)->isMarked)
    {

            model.delPricePosition(3, lsProperty.at(i)->contra);
            lsProperty.at(i)->isLoad = false; 
        }
    }
    tableProxyModel->setSourceModel(model.getTableModel()); //again use proxy
    ui->tableView->resizeColumnsToContents();
    ui->tableView->resizeRowsToContents();
    

    @
    Removing move very fast. But I think it is not right resolving
    }



  • Are you correctly calling beginRemoveRows() and endRemoveRows() in your model implementation? These are needed to notify any connected proxies and views that the source model is changing.



  • In source model:
    @
    bool CatalogTableModel::removeRows (int row, int count, const QModelIndex &parent)
    {
    //TODO:
    Q_UNUSED (parent);
    beginRemoveRows(QModelIndex(), row, row + count - 1);
    //int column = row + count;
    for (int i = 0; i < count; ++i)
    {
    delete listItem->at(row);
    listItem->removeAt(row);
    }
    emit layoutChanged();
    endRemoveRows();
    return true;
    }
    @
    I don't sure correctly it is or not..



  • I don't think that you need to emit the layoutChanged() signal. The {begin|end}RemoveRows() functions do everything that is needed for you. Try removing that emit end see if it works then.

    I suspect what is happening is that the proxy and views are receiving signals from you and from {begin|end}RemoveRows() in an unexpected order which causes them to misbehave.



  • With out emit the layoutChanged() programm to crash!
    Maybe link between source model and proxy is not correct?



  • Setting a proxy is a one-liner so I doubt it is that. I suspect you have a problem in your model. I suggest that you run your model with the qmodeltest helper which diagnoses common model implementation problems. This "wiki article":http://developer.qt.nokia.com/wiki/Model_Test describes how to use qmodeltest and the source can be downloaded from "gitorious":https://qt.gitorious.org/qt/qt/trees/4.7/tests/auto/modeltest.



  • Let's me stupid question - gitotious of model test have pro-file, but wiki article say about pri-file. How I can get pri file?



  • Ah the instructions must be slightly out of date wrt the code. It used to be in it's own repository on Qt-labs. I think you can just copy the modeltest.{h,cpp} files to a little test project and instatiate them as shown in the above wiki article.



  • I use modeltest and have next message:

    ratbr QModelIndex(-1,-1,0x0,QObject(0x0) ) 73 73
    rr QModelIndex(-1,-1,0x0,QObject(0x0) ) 73 73

    and other simular.
    Is it Ok? I check mapping index from proxy to source - it ok.
    What else I may do?



  • Debug your removeRows() function to make sure it is working properly. Step through it in a debugger to see where it crashes and why.



  • I get error:
    @
    void QSortFilterProxyModelPrivate::source_items_removed(
    const QModelIndex &source_parent, int start, int end, Qt::Orientation orient)
    {
    if ((start < 0) || (end < 0))
    return;
    IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
    if (it == source_index_mapping.constEnd()) {
    // Don't care, since we don't have mapping for this index
    return;
    }

    Mapping *m = it.value();
    QVector<int> &source_to_proxy = (orient == Qt::Vertical) ? m->proxy_rows : m->proxy_columns;
    QVector<int> &proxy_to_source = (orient == Qt::Vertical) ? m->source_rows : m->source_columns;
    
    if (end >= source_to_proxy.size())
        end = source_to_proxy.size() - 1;
    
    // Shrink the source-to-proxy mapping to reflect the new item count
    int delta_item_count = end - start + 1;
    source_to_proxy.remove(start, delta_item_count); //This crash
    

    @



  • As ZapB said
    [quote author="ZapB" date="1310123242"]Setting a proxy is a one-liner[/quote]unless you must overwrite a standard function of QSortFilterProxyModel.

    Normally removing rows will be done in the sourcemodel. As you do a selection in the view you have to map the indexes getting from the view to the source with
    @// single selection
    QModelIndex SourceIndex = tableProxyModel->mapToSource(viewIndex);

    // multiple selection
    QModelIndexList SourceIndexList;
    for (int i=0; i<viewIndexList.length(); i++)
    {
    SourceIndexList.append(tableProxyModel->mapToSource(viewIndexList[i]));
    }
    @

    Also, if you have multiple selection you must sort the indexlist before removing like this:
    @void CatalogTableModel::removeSelectedRows(const QModelIndexList &indexlist)
    {
    QList<int> rows;
    foreach(const QModelIndex & index, indexlist)
    {
    rows.append(index.row());
    }

    qSort(rows);
    
    int prev = -1;
    for(int i=rows.count()-1; i>=0; i-=1 )
    {
        int current = rows[i];
        if(current != prev)
        {
            QModelIndex _index = createIndex(current, 0);
            removeLabel(_index);
            prev = current;
        }
    }
    

    }
    @

    Cheers,
    Thomas

    Edit: Uuups, little bit late ...


Log in to reply
 

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