In QT, chaining models does not work as expected

2019-05-02 09:22发布

问题:

Alright, I have a really basic QStandardItemModel, filled with some numbers. I managed to display it in a QTableView, it's ok. I created a new model ( subclass either of QAbstractItemModel or QAbstractProxyModel ), which is some kind of a layer of an existing model - it is needed to set the sourcemodel, and this new layer should do some transformations on the real one.

My problem is, that in the top layer, say "layer model" the data( const QModelIndex & index, int role ) member function never called, however i would like to alter the displaying methods by the role parameter.

Here is a sample code, which demonstrates, that the original model's data(index,role) is always called, whilst the layer model's data(index,role) never. Why? How can the QTableView object "skip" the top layer's data(index,role) ?

#include <QtGui/QApplication>
#include <QtGui>
#include <QStandardItemModel>

class MyModel : public QStandardItemModel
{
public:
    MyModel(const int r, const int c, QObject* parent = 0) : QStandardItemModel(r,c,parent) {}
    QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const {
        qDebug() << "mymodel data";
        return this->itemFromIndex(index)->data(role);
    }
};

class MyProxyModel : public QAbstractProxyModel
{
public:

    MyProxyModel(QObject* parent = 0) : QAbstractProxyModel(parent) {}
    QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const {
        return this->sourceModel()->index(row,column,parent);
    }
    QModelIndex parent ( const QModelIndex & index ) const {
        return this->sourceModel()->parent(index);
    }
    QModelIndex mapFromSource ( const QModelIndex & sourceIndex ) const
    {
        return sourceIndex;
    }
    QModelIndex mapToSource ( const QModelIndex & proxyIndex ) const
    {
        return proxyIndex;
    }
    QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const {
        qDebug() << "myproxymodel data";
        return this->sourceModel()->data(index,role);
    }

    int rowCount ( const QModelIndex & parent = QModelIndex() ) const {
        return this->sourceModel()->rowCount(parent);
    }
    int columnCount ( const QModelIndex & parent = QModelIndex() ) const {
        return this->sourceModel()->columnCount(parent);
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc,argv);
    MyModel model(8, 2);
    MyProxyModel mymodel;
    mymodel.setSourceModel(&model);

    QTableView tableView;
    tableView.setModel(&mymodel);

    tableView.horizontalHeader()->setStretchLastSection(true);
    for (int row = 0; row < 8; ++row) {
        for (int column = 0; column < 2; ++column) {
            QModelIndex index = model.index(row, column, QModelIndex());
            model.setData(index, QVariant((row+1) * (column+1)));
        }

    }
    tableView.show();
    return app.exec();
}

回答1:

Because QTableView uses the model index to retrieve the data, probably something like this.

QModelIndex index = model->index(row, column, parentIndex); 
index.data(Qt::DisplayRole);

And you are returning the model index of the source model instead of an index to your proxy model:

QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const {
    return this->sourceModel()->index(row,column,parent);
}

Try to convert the model index into an index to your proxy model

QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const {
    return this->createIndex(row,column,row);
}

Don't forget to rewrite the map to source and map from source functions.


Solution

class MyTableProxyModel : public QAbstractProxyModel
{
    Q_OBJECT
public:
    MyTableProxyModel (QObject* parent = 0) 
        : QAbstractProxyModel(parent) {
    }

    QModelIndex index(int row, int column, const QModelIndex& parent=QModelIndex()) const {
        return createIndex(row,column,row);
    }

    QModelIndex parent(const QModelIndex &index) const {
        //Works only for non-tree models
        return QModelIndex();
    }

    QModelIndex mapFromSource(const QModelIndex &source) const {
        return index(source.row(), source.column(), source.parent());
    }

    QModelIndex mapToSource(const QModelIndex &proxy) const {
        return (sourceModel()&&proxy.isValid())
            ? sourceModel()->index(proxy.row(), proxy.column(), proxy.parent()) 
            : QModelIndex();
    }

    QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
        qDebug() << "myproxymodel data";
        return mapToSource(index).data(role);
    }

    int rowCount ( const QModelIndex & parent = QModelIndex() ) const {
        return sourceModel() ? sourceModel()->rowCount(parent) : 0;
    }

    int columnCount ( const QModelIndex & parent = QModelIndex() ) const {
        return sourceModel() ? sourceModel()->columnCount(parent) : 0;
    }
};