文档似乎对这一点说得很清楚,我已经看到了一堆关于StackOverflow和其他地方的模糊示例代码,所以...如果我有一个实现QAbstractProxyModel的类A和一个实现QAbstractItemModel的类B,并且我在A的一个示例上调用方法setSourceModel(b),其中b是B的一个示例,那么它是否自动处理转发更新信号,例如modelReset、rowsInserted、等等?还是我必须手动连接所有这些?
QAbstractProxyModel
A
QAbstractItemModel
B
setSourceModel(b)
b
modelReset
rowsInserted
pw136qt21#
你说的对,文档在这方面是非常没有帮助的。看看QAbstractProxyModel的源代码,并将其与Qt 5.12中的QSortFilterProxyModel进行比较,得出QAbstractProxyModel根本不处理dataChanged信号的转发!您必须自己处理!最好选择更复杂的模型,如QSortFilterProxyModel或QIdentityProxyModel。它会为你转发邮件。但是如果你真的无法绕过它,那么它可能是这样的:
/** * Proxy model which only returns one data row of the underlying QAbstractItemModel * except for the first column. Can be used to separate a model for QTreeView into * the tree column and the data columns. This proxy returns the data columns. * Can't use QSortFilterProxyModel because it does not allow for only showing one * row if its parent is filtered out. */ class SingleRowProxy : public QAbstractProxyModel { Q_OBJECT; using BaseType = QAbstractProxyModel; static constexpr auto FIRST_DATA_COLUMN = 1; public: SingleRowProxy( QAbstractItemModel* sourceModel, int row, const QModelIndex& parentIndex, QObject* parentObject = nullptr ) : BaseType( parentObject ), m_sourceRow( row ), m_sourceParent( parentIndex ) { Q_ASSERT( sourceModel != nullptr ); setSourceModel( sourceModel ); } void setSourceModel( QAbstractItemModel *newSourceModel ) override { if ( newSourceModel == sourceModel() ) { return; } beginResetModel(); disconnect( newSourceModel, nullptr, this, nullptr ); BaseType::setSourceModel( newSourceModel ); connect( newSourceModel, &QAbstractItemModel::dataChanged, this, &SingleRowProxy::sourceDataChanged ); connect( newSourceModel, &QAbstractItemModel::modelAboutToBeReset, this, [this] () { beginResetModel(); } ); connect( newSourceModel, &QAbstractItemModel::modelReset, this, [this] () { endResetModel(); } ); } QModelIndex mapFromSource( const QModelIndex& sourceIndex ) const override { if ( !sourceIndex.isValid() || ( sourceIndex.column() < FIRST_DATA_COLUMN ) ) { return {}; } return index( 0, sourceIndex.column() - FIRST_DATA_COLUMN, QModelIndex() ); } QModelIndex mapToSource( const QModelIndex& proxyIndex ) const override { if ( !proxyIndex.isValid() ) { return {}; } return sourceModel()->index( m_sourceRow, proxyIndex.column() + FIRST_DATA_COLUMN, m_sourceParent ); } QVariant data( const QModelIndex& index, int role ) const override { return sourceModel()->data( mapToSource( index ), role ); } int rowCount( [[maybe_unused]] const QModelIndex& parent = QModelIndex() ) const override { return sourceModel()->hasIndex( m_sourceRow, FIRST_DATA_COLUMN, m_sourceParent ) ? 1 : 0; } int columnCount( [[maybe_unused]] const QModelIndex& parent = QModelIndex() ) const override { return sourceModel()->columnCount( sourceModel()->index( m_sourceRow, 0, m_sourceParent ) ); } QModelIndex index( int row, int column, const QModelIndex& parent ) const override { if ( !hasIndex( row, column, parent ) ) { return {}; } return createIndex( row, column ); } QModelIndex parent( [[maybe_unused]] const QModelIndex& child ) const override { return {}; } private slots: /** * QSortFilterProxyModel does it for us but QAbstractProxyModel does not! * So we have to map the source indices and reemit the dataChanged signal. */ void sourceDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles ) { if ( !topLeft.isValid() || !bottomRight.isValid() || ( topLeft.parent() != bottomRight.parent() ) ) { return; } const auto& parent = topLeft.parent(); int minRow = std::numeric_limits<int>::max(); int maxRow = std::numeric_limits<int>::lowest(); int minCol = std::numeric_limits<int>::max(); int maxCol = std::numeric_limits<int>::lowest(); bool foundValidIndex = false; for ( int sourceRow = topLeft.row(); sourceRow <= bottomRight.row(); ++sourceRow ) { for ( int sourceColumn = topLeft.column(); sourceColumn <= bottomRight.column(); ++sourceColumn ) { const auto index = mapFromSource( sourceModel()->index( sourceRow, sourceColumn, topLeft.parent() ) ); if ( !index.isValid() ) { continue; } minRow = std::min( minRow, index.row() ); maxRow = std::max( maxRow, index.row() ); minCol = std::min( minCol, index.column() ); maxCol = std::max( maxCol, index.column() ); foundValidIndex = true; } } if ( foundValidIndex ) { emit dataChanged( index( minRow, minCol, parent ), index( maxRow, maxCol, parent ), roles ); } } private: const int m_sourceRow; const QModelIndex m_sourceParent; };
您必须执行index mapping,因为代理索引与源模型索引不同!请注意,这个示例只是非常基本的,可能不适用于任意Map。请注意,对于完整的代理模型,您必须Map并转发所有信号。QSortFilterProxyModel在setSourceModel中重新连接这些信号:
ufj5ltwl2#
来自文档:要子类化QAbstractProxyModel,需要实现mapFromSource()和mapToSource()。只有在需要不同于默认行为的行为时,才需要重新实现mapSelectionFromSource()和mapSelectionToSource()函数。这里没有关于信号的词。在提到的方法的文档中也是如此。这意味着你不需要关心信号,它们会自动发出。
vx6bjr1n3#
如果class A : public QAbstractProxyModel,class B : public QAbstractItemModel,一些信号转发和更新良好(dataChanged等)。但一些(如rowsInserted)你必须手动连接。我使用的代码如下:
class A : public QAbstractProxyModel
class B : public QAbstractItemModel
dataChanged
... #define COL_ID 0 void A::setSourceModel(QAbstractItemModel *newSourceModel) { beginResetModel(); if (this->sourceModel()) { // disconnect sourceModel signals ... } ... QAbstractProxyModel::setSourceModel(newSourceModel); if (this->sourceModel()) { // connect sourceModel signals ... connect(this->sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(sourceRowsInserted(QModelIndex, int, int))); ... } return; } ... void A::sourceRowsInserted(const QModelIndex &parent, int first, int last) { QModelIndex parentIndex = this->mapFromSource(parent); QModelIndex sourceTopIndex = this->sourceModel()->index(first, COL_ID, parent); QModelIndex sourceBottomIndex = this->sourceModel()->index(last, COL_ID, parent); QModelIndex topIndex = this->mapFromSource(sourceTopIndex); QModelIndex bottomIndex = this->mapFromSource(sourceBottomIndex); beginInsertRows(parentIndex, topIndex.row(), bottomIndex.row()); endInsertRows(); return; }
...
iq3niunx4#
如果类像class A : public QAbstractProxyModel和class B : public QAbstractItemModel,那么信号和插槽也应该被继承。(除非你想让它有特殊的行为。)如果“QAbstractClasses”是A和B的简单成员,则必须“转发”它们
4条答案
按热度按时间pw136qt21#
你说的对,文档在这方面是非常没有帮助的。看看QAbstractProxyModel的源代码,并将其与Qt 5.12中的QSortFilterProxyModel进行比较,得出QAbstractProxyModel根本不处理dataChanged信号的转发!您必须自己处理!最好选择更复杂的模型,如QSortFilterProxyModel或QIdentityProxyModel。它会为你转发邮件。但是如果你真的无法绕过它,那么它可能是这样的:
您必须执行index mapping,因为代理索引与源模型索引不同!
请注意,这个示例只是非常基本的,可能不适用于任意Map。
请注意,对于完整的代理模型,您必须Map并转发所有信号。QSortFilterProxyModel在setSourceModel中重新连接这些信号:
ufj5ltwl2#
来自文档:
要子类化QAbstractProxyModel,需要实现mapFromSource()和mapToSource()。只有在需要不同于默认行为的行为时,才需要重新实现mapSelectionFromSource()和mapSelectionToSource()函数。
这里没有关于信号的词。在提到的方法的文档中也是如此。这意味着你不需要关心信号,它们会自动发出。
vx6bjr1n3#
如果
class A : public QAbstractProxyModel
,class B : public QAbstractItemModel
,一些信号转发和更新良好(dataChanged
等)。但一些(如rowsInserted
)你必须手动连接。我使用的代码如下:
...
iq3niunx4#
如果类像
class A : public QAbstractProxyModel
和class B : public QAbstractItemModel
,那么信号和插槽也应该被继承。(除非你想让它有特殊的行为。)如果“QAbstractClasses”是
A
和B
的简单成员,则必须“转发”它们