由于我应该只对模型进行少量修改,因此透明代理将是最好的起点。该代理将被插入模型链中。
制作代理模型的最简洁方法是什么,以使源模型和目标模型之间的所有事情在两个方向上都保持不变?
是否有任何琐碎index()
的 , mapToSource()
, mapFromSource()
, ... 翻译使用sourceModel()
?
我需要扩展的只是data()
角色和flags()
.
我知道这应该很容易制作,但我不想让它变得不必要的复杂,通过边做边学。
谢谢你的时间。
如果您使用的是 Qt 4.8 或更高版本,则可以使用QIdentityProxyModel,它正是这样做的:它直接映射源模型而不更改结构,因此您可以重新实现data()
和/或flags()
修改返回的内容。
在 Qt 4.7 和更早版本中,最简单的方法应该是重新实现QSortFilterProxyModel而不使用任何排序或过滤,只需覆盖data()
and flags()
。
以下在 Qt 4.7 下工作,并显示了保留 a 语义的最小实现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"
我已经制定了自己的解决方案。它不会进行任何索引更改。实际上,它不应该。信号被传输。
如果将此模型插入其中,则模型链不应受到影响。
#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()
.
我希望这是有帮助的。