Qt5:使用 QSortFilterProxyModel 时的拖放

Posted

技术标签:

【中文标题】Qt5:使用 QSortFilterProxyModel 时的拖放【英文标题】:Qt5: Drag and Drop when using QSortFilterProxyModel 【发布时间】:2017-07-27 11:20:43 【问题描述】:

我有一个从 QAbstractListModel 子类化的模型,它具有不同的 Listviews,这些 Listviews 使用子类 QSortFilterProxyModel 对每个视图进行过滤。当用户单击排序按钮时,可以对视图中的数据进行排序。

我在 QSortFilterProxyModel 中实现了拖放,以在将数据拖放到新的 Listview 时更改数据的状态。这很好用,但是,手动对列表视图中的项目进行排序会导致所有其他显示相同数据的列表视图也被排序,这不是我想要的。

例如,视图 1 显示所有参与者,视图 2 显示处于活动状态的参与者。当从视图 1 拖动到视图 2 时,参与者变为活动状态。如果我手动对参与者进行排序,那么活跃的参与者的索引也会被排序。但是,如果我使用 proxyModel->sort() 方法自动对它们进行排序,则不会发生这种情况。

如何在不更改源模型索引的情况下手动重新排列代理模型中的数据?

示例代码:

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() 可能有更好的方法来做到这一点:

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();
   

在 DataModel 中,我还实现了以下方法:

 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;

【问题讨论】:

你能显示DataModel::addData吗? 是的,我将其添加到问题中。 如果没有 mapToSource() 父级,您不能传递 parent.row() 并确保它有效。 好的,但我仍然只是更新 sourceModel 的索引,而不仅仅是代理模型。我想知道是否应该在 ProxyModel 中实现 mimeData 方法并以某种方式跟踪代理过滤的项目,但这对我来说并不明显。 不幸的是,如果不查看模型实现,就很难看出问题所在。 QAbstractItemModel 子类化是一个雷区 【参考方案1】:

不要修改DataModel

QSortFilterProxyModel 的意义在于,您可以提供完全独立于基础数据的任何其他视图的新排序。让该视图移动底层行会破坏这一点。

您应该编写一个代理来手动覆盖排序,记录每一行的位置。这可以使用QSortFilterProxyModel 作为其来源,而后者又来自DataModel

【讨论】:

我希望对数据进行手动和自动排序,例如单击调用“proxyModel->sort()”的按钮时,按名称按字母顺序自动对数据进行排序。这就是为什么设置了“setDynamicSortFilter(false)”并且已经为字母排序实现了“lessThan”。 那么你想要另一个代理层:DataModel -> QSortFilterProxyModel -> DragDropReorderProxyModel 其中 DragDropReorderProxyModel 派生 QAbstractProxyModel 并实现 mapFromSource / mapToSource 这是个好主意,谢谢。我不认为你可以分层代理。我会试试的。

以上是关于Qt5:使用 QSortFilterProxyModel 时的拖放的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Qt5 中使用 OpenCV

使用 Visual Studio 2015 设置 Qt5

使用 Qt5 查找可执行文件

Qt5如何安装与使用

Qt5如何安装与使用

Qt5.5 脚本