Drag & Drop в QAbstractItemModel

125
05 октября 2019, 01:40

Не могу разобраться в том, как работает drag & drop в qt-ишной model-view. Мне нужно реализовать перетаскивание между двумя view (QTreeView) в которых находятся разные модели (но с данными одного типа). Элементы захватываются в одном view и перетаскиваются во второй:

ui_->componentsView->setDragDropMode(
  QAbstractItemView::DragDropMode::DragOnly);
ui_->componentsView->setDragEnabled(true);
ui_->devicesView->setDragDropMode(QAbstractItemView::DragDropMode::DropOnly);
ui_->devicesView->setAcceptDrops(true);
ui_->devicesView->setDropIndicatorShown(true);

При этоп "дропнуть" данные мы можем только в уже существующий элемент второй модели, данные в котором изменяться соответствующим образом:

bool DeviceModelAdapter::canDropMimeData(const QMimeData *data,
                                         Qt::DropAction /*action*/,
                                         int /*row*/,
                                         int /*column*/,
                                         const QModelIndex &parent) const {
  if (auto mimeData = dynamic_cast<const NodeMimeData *>(data);
      mimeData && parent.isValid()) {
    return true;
  }
  return false;
}
QStringList DeviceModelAdapter::mimeTypes() const {
  return {NodeMimeData::mimeType()};// перетаскиваемый тип, содержит
 // указатели на некоторые объеты, которые добавятся к объетку, хранимому в модели
}
Qt::ItemFlags DeviceModelAdapter::flags(const QModelIndex &index) const {
  if (index.isValid()) {
    return ModelAdapter::flags(index) | Qt::ItemIsDropEnabled;
  }
  return {};
}
bool DeviceModelAdapter::dropMimeData(const QMimeData *data,
                                    Qt::DropAction /*action*/,
                                    int row,
                                    int /*column*/,
                                    const QModelIndex &parent) {
  if (auto nodeData = dynamic_cast<const NodeMimeData *>(data);
      nodeData && !parent.isValid()) {
    auto curIndex = index(row, 0);
    if (auto node = getNodeFromIndex(curIndex)) {
      for (auto &i : nodeData->ptrList()) {
        if (i) {
          i->setParent(node);
        }
      }
    }
    emit dataChanged(curIndex, curIndex);
  }
  return false;
}

Однако при навелении курсора на элемент в модели (куда должен происходить drop) курсор показывает, что невозможно дропнуть данные. При этом если в методе canDropMimeData меняю parent.isValid() на !parentIsValid(), а в методе flags возвращаю при невалидном индексе Qt::ItemIsDropEnabled, то курсор показывает, что перетаскивание возможно в любой части view, но только не в уже существующий элемент. Где я ошибся?

UDP потестив немного я понял, что drop на самом деле работает, а проблема заключается в другом (не в модели). Если при первом наведении во view мышь не попала на элемент, то метод canDropMimeData вызывается только один раз и сколько мы не двигаем мышь - он вызываться не будет. Но если первый раз мы попали на элемент, то вызов происходит при каждом движении мыши. Извиняюсь если описал не понятно, потому что я и сам не понимаю, как такое вообще проиходит.

Answer 1

Собственно, в чем оказалась проблема: я использовал свой унаследованный от qmimedata класс, который не использовал метод setdata, в результате чего, при проверке dragenterevent не сходились типы моего mime и того, что может обрабатывать модель. Но! Несмотря на это, в этом случае (конкретно для dragenterevent), проверка на этом не заканчивается, а передаеться методу модели candropdata. Как результат - неожиданное (противоречивое) поведение программы.

READ ALSO
Запретить редактирование JTable [закрыт]

Запретить редактирование JTable [закрыт]

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

108
return не возвращает значение [закрыт]

return не возвращает значение [закрыт]

Не могу понять почему метод say(String something) не возвращает "Ты чё не знаешь, что рыбы не разговаривают?"

122
Кнопка назад Toolbar

Кнопка назад Toolbar

Прочел документацию, и сделал как написано AndroidManifestxml

108
Почему у меня не работает аннотация @PreRemove?

Почему у меня не работает аннотация @PreRemove?

Пишу интеграционные тесты для сервисов с базой в памяти (h2)Сервисы не содержат никакой логики и просто вызывают аналогичные методы у ДАО

115