plover plover - 1 year ago 275
C++ Question

Qt5: Drag and Drop when using QSortFilterProxyModel

I have a model that is sub-classed from

QAbstractListModel
that has different Listviews that are filtered using a sub-classed QSortFilterProxyModel for each view. The data in the view can be sorted when the user clicks a sort button.

I implemented the drag and drop inside the QSortFilterProxyModel, to change the state of the data when it is dropped into a new Listview. This works fine, however, manually sorting the items within a listview causes all other listview displaying the same data to be sorted as well, which is not what I want.

Example, View 1 shows all participants, view 2 shows participants that are active. When dragged from view 1 to view 2, the participant becomes active. If I manually sort the participants, the index of participants that are active also get sorted. This does however not happen if I automatically sort them using
proxyModel->sort()
method.

How can I manually rearrange the data in the proxy model without changing the index in the source model?

Example code:

MySortFilterProxyModel::MySortFilterProxyModel(bool active, QObject *parent ) :
QSortFilterProxyModel( parent ),
m_filter( "" ),
m_active(active)
{
setDynamicSortFilter( false );
}

void MySortFilterProxyModel::setFilter( QString filter )
{
m_filter = filter;
invalidateFilter();
}

Qt::ItemFlags MySortFilterProxyModel::flags( const QModelIndex &index ) const
{
if( index.isValid() )
{
return ( QSortFilterProxyModel::flags( index ) | Qt::ItemIsDragEnabled | Qt::ItemIsEditable );
}

return Qt::ItemIsDropEnabled | QSortFilterProxyModel::flags( index );
}

bool MySortFilterProxyModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
{


if( !data->hasFormat( dataModel::dataMimeType() ) )
{
return false;
}

if( action == Qt::IgnoreAction )
{
return true;
}

if( column > 0 )
{
return false;
}

QByteArray encodeData = data->data(dataModel::dataMimeType());
QDataStream stream( &encodeData, QIODevice::ReadOnly );

while( !stream.atEnd() )
{
DataRecord *dr = new DataRecord();
stream >> dr;

dr->setActive( m_active );

// AddData method in the dataModel removes duplicate rows and inserts the data into the correct row.
qobject_cast< DataModel * >( sourceModel() )->addData( fdr, parent.row() );

}
return true;
}


Edit addData() There is propably a better way to do this:

void DataModel::addData( DataRecord *dr, int row )
{
int i =0;
for( auto const& itr : m_dataRecords )
{
if( itr->getUniqueID() == dr->getUniqueID() )
{
break;
}
++i;
}
removeRows( i, 1, QModelIndex() );

beginInsertRows( QModelIndex(), row, row );
m_dataRecords.insert( row, dr );
endInsertRows();
}


In the DataModel I also implement the following methods:

QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override;

bool setData( const QModelIndex & index, const QVariant & value, int role=Qt::EditRole );

bool removeRows( int row, int count, const QModelIndex &parent ) override;

QMimeData *mimeData( const QModelIndexList &indexes ) const override;

QStringList mimeTypes() const override;

int rowCount( const QModelIndex &parent ) const override;

Qt::DropActions supportedDropActions() const override;

Answer Source

Don't modify DataModel

The point of QSortFilterProxyModel is that you can provide a new ordering completely independently of any other view on the underlying data. Having that view move the underlying rows around breaks that.

You should instead write a proxy that provides the manual override of ordering, recording where each row should be positioned. This can use the QSortFilterProxyModel as its source, which in turn sources from DataModel

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download