如何制作透明代理模型:QAbstractProxyModel?

时间:2013-11-07 12:12:25

标签: c++ qt model

由于我只对模型进行少许修改,因此透明代理将是最佳起点。此代理将插入到模型链中。

制作代理模型最简洁的方法是什么,在两个方向上源和目标模型之间的每一件事都没有改变?

使用index()是否有任何简单的mapToSource()mapFromSource()sourceModel(),...翻译?

我需要扩展的是data()角色和flags()

我知道这应该很容易制作,但我不想让它变得多么复杂,并且通过实践来学习。

Thanx你的时间。

3 个答案:

答案 0 :(得分:5)

如果您使用的是Qt 4.8或更高版本,则可以使用QIdentityProxyModel,它完全相同:它直接映射源模型而不更改结构,因此您可以重新实现data()和/或flags()修改返回的内容。

在Qt 4.7及更早版本中,最简单的方法是重新实现QSortFilterProxyModel而不使用任何排序或过滤,只需覆盖data()flags()

答案 1 :(得分:2)

以下内容在Qt 4.7下工作,并显示了保留QAbstractItemModel语义的最小实现:

// https://github.com/KubaO/stackoverflown/tree/master/questions/transparent-proxy-19835618
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif

class TransparentProxyModel : public QAbstractProxyModel {
   Q_OBJECT
   struct Helper : QAbstractItemModel {
      using QAbstractItemModel::createIndex;
   };
   struct Op {
      enum Kind { AddRow, RemoveRow, AddCol, RemoveCol, MoveRow, MoveCol } kind;
      QModelIndex parentSrc;
      int first, last;
      QModelIndex parentDst;
      int index;
      bool checkSrc(Kind k, const QModelIndex &i, int f, int l) const {
         return kind == k && parentSrc == i && first == f && last == l;
      }
      bool checkDst(const QModelIndex &i, int n) const {
         return parentDst == i && index == n;
      }
   };
   QVector<Op> m_addsRemoves;
   QModelIndex createSourceIndex(int r, int c, void *data) const {
      return static_cast<Helper *>(sourceModel())->createIndex(r, c, data);
   }
   Q_SLOT void onDataChanged(const QModelIndex &tl, const QModelIndex &br) {
      emit dataChanged(mapFromSource(tl), mapFromSource(br));
   }
   Q_SLOT void onRowsAboutToBeInserted(const QModelIndex &parent, int first, int last) {
      m_addsRemoves.push_back({Op::AddRow, parent, first, last});
      beginInsertRows(mapFromSource(parent), first, last);
   }
   Q_SLOT void onRowsInserted(const QModelIndex &parent, int first, int last) {
      Q_ASSERT(!m_addsRemoves.isEmpty());
      Q_ASSERT(m_addsRemoves.last().checkSrc(Op::AddRow, parent, first, last));
      m_addsRemoves.pop_back();
      endInsertRows();
   }
   Q_SLOT void onRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last) {
      m_addsRemoves.push_back({Op::RemoveRow, parent, first, last});
      beginRemoveRows(mapFromSource(parent), first, last);
   }
   Q_SLOT void onRowsRemoved(const QModelIndex &parent, int first, int last) {
      Q_ASSERT(!m_addsRemoves.isEmpty());
      Q_ASSERT(m_addsRemoves.last().checkSrc(Op::RemoveRow, parent, first, last));
      m_addsRemoves.pop_back();
      endRemoveRows();
   }
   Q_SLOT void onColumnsAboutToBeInserted(const QModelIndex &parent, int first,
                                          int last) {
      m_addsRemoves.push_back({Op::AddCol, parent, first, last});
      beginInsertColumns(parent, first, last);
   }
   Q_SLOT void onColumnsInserted(const QModelIndex &parent, int first, int last) {
      Q_ASSERT(!m_addsRemoves.isEmpty());
      Q_ASSERT(m_addsRemoves.last().checkSrc(Op::AddCol, parent, first, last));
      m_addsRemoves.pop_back();
      endInsertColumns();
   }
   Q_SLOT void onColumnsAboutToBeRemoved(const QModelIndex &parent, int first, int last) {
      m_addsRemoves.push_back({Op::RemoveCol, parent, first, last});
      beginRemoveColumns(mapFromSource(parent), first, last);
   }
   Q_SLOT void onColumnsRemoved(const QModelIndex &parent, int first, int last) {
      Q_ASSERT(!m_addsRemoves.isEmpty());
      Q_ASSERT(m_addsRemoves.last().checkSrc(Op::RemoveCol, parent, first, last));
      m_addsRemoves.pop_back();
      endRemoveColumns();
   }
   Q_SLOT void onRowsAboutToBeMoved(const QModelIndex &srcParent, int start, int end,
                                    const QModelIndex &dstParent, int row) {
      m_addsRemoves.push_back({Op::MoveRow, srcParent, start, end, dstParent, row});
      beginMoveRows(mapFromSource(srcParent), start, end, mapFromSource(dstParent), row);
   }
   Q_SLOT void onRowsMoved(const QModelIndex &srcParent, int start, int end,
                           const QModelIndex &dstParent, int row) {
      Q_ASSERT(!m_addsRemoves.isEmpty());
      auto const &op = m_addsRemoves.last();
      Q_ASSERT(op.checkSrc(Op::MoveRow, srcParent, start, end) &&
               op.checkDst(dstParent, row));
      m_addsRemoves.pop_back();
      endMoveRows();
   }
   Q_SLOT void onColumnsAboutToBeMoved(const QModelIndex &srcParent, int start, int end,
                                       const QModelIndex &dstParent, int col) {
      m_addsRemoves.push_back({Op::MoveCol, srcParent, start, end, dstParent, col});
      beginMoveColumns(mapFromSource(srcParent), start, end, mapFromSource(dstParent),
                       col);
   }
   Q_SLOT void onColumnsMoved(const QModelIndex &srcParent, int start, int end,
                              const QModelIndex &dstParent, int col) {
      Q_ASSERT(!m_addsRemoves.isEmpty());
      auto const &op = m_addsRemoves.last();
      Q_ASSERT(op.checkSrc(Op::MoveRow, srcParent, start, end) &&
               op.checkDst(dstParent, col));
      m_addsRemoves.pop_back();
      endMoveColumns();
   }

  public:
   TransparentProxyModel(QObject *parent = nullptr) : QAbstractProxyModel(parent) {}
   QModelIndex mapFromSource(const QModelIndex &src) const override {
      if (!src.isValid() || !sourceModel()) return {};
      Q_ASSERT(src.model() == sourceModel());
      return createIndex(src.row(), src.column(), src.internalPointer());
   }
   QModelIndex mapToSource(const QModelIndex &prx) const override {
      if (!prx.isValid() || !sourceModel()) return {};
      Q_ASSERT(prx.model() == this);
      return createSourceIndex(prx.row(), prx.column(), prx.internalPointer());
   }
   QModelIndex index(int row, int column, const QModelIndex &parent) const override {
      if (!sourceModel()) return {};
      Q_ASSERT(!parent.isValid() || parent.model() == this);
      return mapFromSource(sourceModel()->index(row, column, mapToSource(parent)));
   }
   int rowCount(const QModelIndex &parent) const override {
      if (!sourceModel()) return 0;
      Q_ASSERT(!parent.isValid() || parent.model() == this);
      return sourceModel()->rowCount(mapToSource(parent));
   }
   int columnCount(const QModelIndex &parent) const override {
      if (!sourceModel()) return 0;
      Q_ASSERT(!parent.isValid() || parent.model() == this);
      return sourceModel()->columnCount(mapToSource(parent));
   }
   QModelIndex parent(const QModelIndex &child) const override {
      if (!child.isValid() || !sourceModel()) return {};
      Q_ASSERT(child.model() == this);
      return mapFromSource(sourceModel()->parent(mapToSource(child)));
   }
   void setSourceModel(QAbstractItemModel *model) override {
      if (sourceModel()) disconnect(sourceModel(), 0, this, 0);
      QAbstractProxyModel::setSourceModel(model);
      if (!sourceModel()) return;
      connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this,
              SLOT(onDataChanged(QModelIndex, QModelIndex)));
      connect(model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), this,
              SIGNAL(headerDataChanged(Qt::Orientation, int, int)));
      connect(model, SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged()));
      connect(model, SIGNAL(layoutAboutToBeChanged()), this,
              SIGNAL(layoutAboutToBeChanged()));
      connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this,
              SLOT(onRowsAboutToBeInserted(QModelIndex, int, int)));
      connect(model, SIGNAL(rowsInserted(QModelIndex, int, int)), this,
              SLOT(onRowsInserted(QModelIndex, int, int)));
      connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this,
              SLOT(onRowsAboutToBeRemoved(QModelIndex, int, int)));
      connect(model, SIGNAL(rowsRemoved(QModelIndex, int, int)), this,
              SLOT(onRowsRemoved(QModelIndex, int, int)));
      connect(model, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)), this,
              SLOT(onColumnsAboutToBeInserted(QModelIndex, int, int)));
      connect(model, SIGNAL(columnsInserted(QModelIndex, int, int)), this,
              SLOT(onColumnsInserted(QModelIndex, int, int)));
      connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)), this,
              SLOT(onColumnsAboutToBeRemoved(QModelIndex, int, int)));
      connect(model, SIGNAL(columnsRemoved(QModelIndex, int, int)), this,
              SLOT(onColumnsRemoved(QModelIndex, int, int)));
      connect(model, SIGNAL(modelAboutToBeReset()), this, SIGNAL(modelAboutToBeReset()));
      connect(model, SIGNAL(modelReset()), this, SIGNAL(modelReset()));
      connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)),
              this, SLOT(onRowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)));
      connect(model, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), this,
              SLOT(onRowsMoved(QModelIndex, int, int, QModelIndex, int)));
      connect(
          model, SIGNAL(columnsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)),
          this, SLOT(onColumnsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)));
      connect(model, SIGNAL(columnsMoved(QModelIndex, int, int, QModelIndex, int)), this,
              SLOT(onColumnsMoved(QModelIndex, int, int, QModelIndex, int)));
   }
};

int main(int argc, char *argv[]) {
   QApplication app(argc, argv);
   QFileSystemModel model;
   TransparentProxyModel proxy;
   proxy.setSourceModel(&model);
   QTreeView view;
   view.setModel(&proxy);
   model.setRootPath(QDir::homePath());
   view.setRootIndex(proxy.mapFromSource(model.index(QDir::homePath())));
   view.show();
   return app.exec();
}
#include "main.moc"

答案 2 :(得分:1)

我已经制定了自己的解决方案。它不会使任何索引发生变化。实际上它不应该。 信号被转移。

如果将此模型插入模型链中,则不应影响模型链。

#ifndef TTRANSPARENTPROXYMODEL_H
#define TTRANSPARENTPROXYMODEL_H

#include <QAbstractProxyModel>

class TTransparentProxyModel :
    public QAbstractProxyModel
{
    Q_OBJECT
public:
    TTransparentProxyModel(QObject *parent = 0);

    void setSourceModel(QAbstractItemModel* newSourceModel);

    /* QAbstractProxyModel methods */
    virtual QModelIndex index( int, int c = 0, const QModelIndex& parent = QModelIndex() ) const;
    virtual QModelIndex parent( const QModelIndex &child ) const;
    virtual int rowCount( const QModelIndex &idx = QModelIndex() ) const;
    virtual int columnCount(const QModelIndex &parent ) const;
    virtual QModelIndex mapToSource( const QModelIndex &index ) const;
    virtual QModelIndex mapFromSource( const QModelIndex &idx ) const;
};

#endif // TTRANSPARENTPROXYMODEL_H

和cpp文件:

#include "TransparentProxyModel.h"

TTransparentProxyModel::TTransparentProxyModel(QObject *parent)
    : QAbstractProxyModel(parent)
{
}

void TTransparentProxyModel::setSourceModel(QAbstractItemModel* newSourceModel)
{
    beginResetModel();

    if (sourceModel()) {
    disconnect(sourceModel(), SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
               this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)));
    disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
               this, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
    disconnect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
               this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)));
    disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
               this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
    disconnect(sourceModel(), SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
               this, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
    disconnect(sourceModel(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
               this, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
    disconnect(sourceModel(), SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)),
               this, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)));
    disconnect(sourceModel(), SIGNAL(columnsInserted(const QModelIndex &, int, int)),
               this, SIGNAL(columnsInserted(const QModelIndex &, int, int)));
    disconnect(sourceModel(), SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)),
               this, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)));
    disconnect(sourceModel(), SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
               this, SIGNAL(columnsRemoved(const QModelIndex &, int, int)));
    disconnect(sourceModel(), SIGNAL(columnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
               this, SIGNAL(columnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
    disconnect(sourceModel(), SIGNAL(columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
               this, SIGNAL(columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
    disconnect(sourceModel(), SIGNAL(modelAboutToBeReset()),
               this, SIGNAL(modelAboutToBeReset()));
    disconnect(sourceModel(), SIGNAL(modelReset()),
               this, SIGNAL(modelReset()));
    disconnect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
               this, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)));
    disconnect(sourceModel(), SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
               this, SIGNAL(headerDataChanged(Qt::Orientation,int,int)));
    disconnect(sourceModel(), SIGNAL(layoutAboutToBeChanged()),
               this, SIGNAL(layoutAboutToBeChanged()));
    disconnect(sourceModel(), SIGNAL(layoutChanged()),
               this, SIGNAL(layoutChanged()));
    }

    QAbstractProxyModel::setSourceModel(newSourceModel);

    if (sourceModel()) {
    connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
            this, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)));
    connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
            this, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
    connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
            this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)));
    connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
            this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
    connect(sourceModel(), SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
            this, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
    connect(sourceModel(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
            this, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
    connect(sourceModel(), SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)),
            this, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)));
    connect(sourceModel(), SIGNAL(columnsInserted(const QModelIndex &, int, int)),
            this, SIGNAL(columnsInserted(const QModelIndex &, int, int)));
    connect(sourceModel(), SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)),
            this, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)));
    connect(sourceModel(), SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
            this, SIGNAL(columnsRemoved(const QModelIndex &, int, int)));
    connect(sourceModel(), SIGNAL(columnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
            this, SIGNAL(columnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
    connect(sourceModel(), SIGNAL(columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)),
            this, SIGNAL(columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)));
    connect(sourceModel(), SIGNAL(modelAboutToBeReset()),
            this, SIGNAL(modelAboutToBeReset()));
    connect(sourceModel(), SIGNAL(modelReset()),
            this, SIGNAL(modelReset()));
    connect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
            this, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)));
    connect(sourceModel(), SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
            this, SIGNAL(headerDataChanged(Qt::Orientation,int,int)));
    connect(sourceModel(), SIGNAL(layoutAboutToBeChanged()),
            this, SIGNAL(layoutAboutToBeChanged()));
    connect(sourceModel(), SIGNAL(layoutChanged()),
            this, SIGNAL(layoutChanged()));
    }
    endResetModel();
}

//virtual int rowCount( const QModelIndex &idx = QModelIndex() ) const;
int TTransparentProxyModel::rowCount(const QModelIndex &parent) const
{
    if(!sourceModel())
    {
    return 0;
    }
    return this->sourceModel()->rowCount(parent);
}

//virtual int columnCount( const QModelIndex &idx ) const;
int TTransparentProxyModel::columnCount(const QModelIndex &parent) const
{
    if(!sourceModel())
    {
    return 0;
    }
    return this->sourceModel()->columnCount(parent);
}

//virtual QModelIndex index( int, int c = 0, const QModelIndex& parent = QModelIndex() ) const;
QModelIndex TTransparentProxyModel::index(int row, int column, const QModelIndex &parent) const
{
    if(!this->sourceModel())
    {
    return QModelIndex();
    }
    return this->sourceModel()->index(row,column,parent);
}

//virtual QModelIndex parent( const QModelIndex &idx ) const;
QModelIndex TTransparentProxyModel::parent(const QModelIndex &child) const
{
//  TODO: check if this is valid.
    QModelIndex mi =  mapFromSource(child);
    if (mi.isValid())
    {
    return mi.parent();
    }
    return QModelIndex();
}

//virtual QModelIndex mapToSource( const QModelIndex &idx ) const;
QModelIndex TTransparentProxyModel::mapToSource(const QModelIndex &index) const
{
    if(!this->sourceModel())
    {
    return QModelIndex();
    }
    return this->sourceModel()->index(index.row(),index.column());
}


//virtual QModelIndex mapFromSource( const QModelIndex &idx ) const;
QModelIndex TTransparentProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
    if(sourceIndex.isValid())
    if(!this->sourceModel())
    {
    return QModelIndex();
    }
    return this->sourceModel()->index(sourceIndex.row(),sourceIndex.column());
}

我试图捕捉来自源的所有信号,并以相同的方式发信号到下一个模型。

唯一的方法,我不确定它是否已正确实施,是parent()

我希望这有用。