Qt5: перетаскивание при использовании QSortFilterProxyModel

У меня есть модель, которая является подклассом из QAbstractListModel, которая имеет разные представления списка, которые отфильтрованы с использованием подкласса QSortFilterProxyModel для каждого представления. Данные в представлении можно сортировать, когда пользователь нажимает кнопку сортировки.

Я реализовал перетаскивание внутри QSortFilterProxyModel, чтобы изменить состояние данных, когда они помещаются в новый список. Это отлично работает, однако ручная сортировка элементов в списке приводит к сортировке всех других списков, отображающих те же данные, а это не то, что я хочу.

Например, в представлении 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;
}

Изменить 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;

person plover    schedule 27.07.2017    source источник
comment
не могли бы вы показать DataModel::addData?   -  person IlBeldus    schedule 27.07.2017
comment
да, я добавил это к вопросу.   -  person plover    schedule 27.07.2017
comment
Вы не можете передать parent.row() без родителя mapToSource() и убедиться, что он действителен.   -  person IlBeldus    schedule 27.07.2017
comment
Хорошо, но я все еще просто обновляю индексы исходной модели, а не только прокси-модели. Мне интересно, должен ли я реализовать метод mimeData в ProxyModel и каким-то образом отслеживать элементы, отфильтрованные прокси, но это не очевидно для меня.   -  person plover    schedule 27.07.2017
comment
К сожалению, трудно понять, что происходит, не видя реализации модели. Подкласс QAbstractItemModel — это минное поле   -  person IlBeldus    schedule 27.07.2017
comment
Было бы слишком много добавлять сюда весь код для модели, но я добавил содержимое заголовочного файла, может быть, отсутствует метод, который мне нужно реализовать?   -  person plover    schedule 27.07.2017


Ответы (1)


Не изменять DataModel

Суть QSortFilterProxyModel в том, что вы можете обеспечить новый порядок совершенно независимо от любого другого представления базовых данных. Наличие этого представления, перемещающего базовые строки, ломает это.

Вместо этого вы должны написать прокси, который обеспечивает ручное переопределение порядка, записывая, где должна быть расположена каждая строка. Это может использовать QSortFilterProxyModel в качестве источника, который, в свою очередь, исходит из DataModel.

person Caleth    schedule 27.07.2017
comment
Я хочу иметь как ручную, так и автоматическую сортировку данных, например. автоматически сортировать данные по имени в алфавитном порядке при нажатии кнопки, которая вызывает «proxyModel-›sort()». Вот почему установлен setDynamicSortFilter(false) и уже реализована функция lessThan для сортировки по алфавиту. - person plover; 27.07.2017
comment
Затем вы хотите иметь еще один прокси-слой: DataModel -> QSortFilterProxyModel -> DragDropReorderProxyModel, где DragDropReorderProxyModel является производным от QAbstractProxyModel и реализует mapFromSource/mapToSource. - person Caleth; 27.07.2017
comment
Это отличная идея, спасибо. Я не думал, что можно наслаивать прокси. Я попробую это. - person plover; 27.07.2017