Виджет ProjectListWidget не только инициирует перенос объектов, он также является местом приема таких объектов, если они приходят от другого виджета ProjectListWidget того же самого приложения. QDragEnterEvent::source возвращает указатель на виджет, который инициирует перенос, если этот виджет принадлежит тому же самому приложению; в противном случае он возвращает нулевой указатель. Мы используем qobject_cast, чтобы убедиться в инициировании переноса виджетом ProjectListWidget. Если все верно, мы указываем Qt на нашу готовность восприятия данного действия как переноса.
01 void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event)
02 {
03 ProjectListWidget *source =
04 qobject_cast(event->source);
05 if (source && source != this) {
07 event->setDropAction(Qt::MoveAction);
08 event->accept;
09 }
10 }
Программный код функции dragMoveEvent идентичен тому, что мы делали в функции dragEnterEvent. Он необходим, потому что нам приходится переопределять реализацию этой функции в классе QListWidget (в действительности в классе QAbstractItemView).
01 void ProjectListWidget::dropEvent(QDropEvent *event)
02 {
03 ProjectListWidget *source =
04 qobject_cast(event->source);
05 if (source && source != this) {
06 addItem(event->mimeData->text);
07 event->setDropAction(Qt::MoveAction);
08 event->accept;
09 }
10 }
В DropEvent мы используем функцию QMimeData::text для получения перенесенного текста и создаем элемент с этим текстом. Нам также необходимо воспринять данное событие как «операцию перетаскивания», чтобы указать исходному виджету на то, что он может теперь удалить первоначальную версию перенесенного элемента.
«Drag-and-drop» — мощный механизм передачи данных между приложениями. Однако в некоторых случаях его можно реализовать, не используя предусмотренные в Qt средства механизма «drag-and-drop». Если нам требуется переносить данные внутри одного виджета некоторого приложения, во многих случаях мы можем просто переопределить функции mousePressEvent и mouseReleaseEvent.
Поддержка пользовательских типов переносимых объектов
До сих пор в представленных примерах мы полагались на поддержку QMimeData распространенных типов MIME. Так, мы вызывали QMimeData::setText для создания объекта переноса текста и использовали QMimeData:urls для получения содержимого объекта переноса типа text/uri-list. Если мы хотим перетаскивать обычный текст, текст в формате HTML, изображения, адреса URL или цвета, мы можем спокойно использовать класс QMimeData. Но если мы хотим перетаскивать пользовательские данные, необходимо сделать выбор между следующими альтернативами:
1. Мы можем обеспечить произвольные данные в виде массива QByteArray, используя функцию QMimeData::setData, и извлекать их позже, используя функцию QMimeData::data.
2. Мы можем создать подкласс QMimeData и переопределить функции formats и retrieveData для обработки наших пользовательских типов данных.
3. Для выполнения операций механизма «drag-and-drop» в рамках одного приложения мы можем создать подкласс QMimeData и хранить данные в любых структурах данных.
Первый подход не требует никаких подклассов, но имеет некоторые недостатки: нам необходимо преобразбвать наши структуры данных в тип QByteArray, даже если переносимый объект не принимается, а если требуется обеспечить несколько MIME—типов, чтобы можно было хорошо взаимодействовать с самыми разными приложениями, нам придется сохранять несколько копий данных (по одной на каждый тип MIME). Если данные имеют большой размер, это может излишне замедлять работу приложения. При использовании второго и третьего подходов можно избежать или свести к минимуму эти проблемы. В этом случае мы получаем полное управление и можем использовать эти два подхода совместно.