如何实现从QTreeWidget拖拽到QML Map的放置功能
问题描述
我编写了包含QML Map的MapWidget类和对应的map.qml文件,地图能正常显示。现在想实现将旁边QTreeWidget的QTreeWidgetItem拖拽到地图上,在鼠标释放位置创建对应项。调试发现拖拽进入QML部件时会触发dragEnterEvent,但释放鼠标时dropEvent未被调用,且地图显示禁止放置的标志。请问哪里出错了,该如何启用放置功能?
MapWidget类代码
头文件
class MapWidget : public QQuickWidget { Q_OBJECT public: MapWidget() = delete; MapWidget(QWidget* parent = nullptr); MapWidget(const MapWidget&) = delete; MapWidget(MapWidget&&) = delete; ~MapWidget() = default; public: MapWidget& operator=(const MapWidget&) = delete; MapWidget& operator=(MapWidget&&) = delete; protected: void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; };
实现文件
MapWidget::MapWidget(QWidget* parent) : QQuickWidget(parent) { setSource(QUrl(QStringLiteral("qrc:/qml/map.qml"))); setResizeMode(QQuickWidget::SizeRootObjectToView); setAcceptDrops(true); QQuickItem* mapItem = dynamic_cast<QQuickItem*>(rootObject()->findChild<QQuickItem*>("myMap")); mapItem->setAcceptHoverEvents(true); } /////////////////////////////////////////////////////////////////////////////// // VIRTUAL PROTECTED SECTION // /////////////////////////////////////////////////////////////////////////////// void MapWidget::dragEnterEvent(QDragEnterEvent* event) { if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist")) { event->accept(); } else { event->ignore(); } } void MapWidget::dropEvent(QDropEvent* event) { if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist")) { QGeoCoordinate returnedValue; auto pos = rootObject()->mapToScene(event->pos()); QQuickItem* mapItem = dynamic_cast<QQuickItem*>(rootObject()->findChild<QQuickItem*>("myMap")); rootObject()->setAcceptHoverEvents(true); QMetaObject::invokeMethod(mapItem, "toCoordinate", Q_RETURN_ARG(QGeoCoordinate, returnedValue), Q_ARG(QPointF, pos)); event->accept(); } else { event->ignore(); } }
map.qml代码
import QtQuick 2.0 import QtQuick.Window 2.14 import QtPositioning 5.15 import QtLocation 5 Item { width: Qt.platform.os == "android" ? Screen.width : 512 height: Qt.platform.os == "android" ? Screen.height : 512 visible: true property double center_latitude: 59.91 property double center_longitude: 10.75 Plugin { id: mapPlugin name: "osm" } Map { objectName: "myMap" anchors.fill: parent plugin: mapPlugin center: QtPositioning.coordinate(center_latitude, center_longitude) zoomLevel: 14 copyrightsVisible: false } }
解决方案
问题核心是仅在C层的QQuickWidget设置拖放支持不够,QML内部的Map控件会拦截拖放事件,需要同时在QML和C层做调整:
修改QML的Map控件,启用拖放并处理事件
在map.qml的Map节点中添加拖放相关属性和事件处理,明确接受合法的拖放请求:Map { objectName: "myMap" anchors.fill: parent plugin: mapPlugin center: QtPositioning.coordinate(center_latitude, center_longitude) zoomLevel: 14 copyrightsVisible: false acceptDrops: true // 启用放置支持 onDragEnter: { if (event.mimeData.hasFormat("application/x-qabstractitemmodeldatalist")) { event.acceptProposedAction(); } else { event.ignore(); } } onDragOver: { // 必须实现此事件,否则系统会判定为禁止放置 if (event.mimeData.hasFormat("application/x-qabstractitemmodeldatalist")) { event.acceptProposedAction(); } else { event.ignore(); } } }调整C++层的dragEnterEvent,使用正确的接受方式
将dragEnterEvent中的event->accept()替换为event->acceptProposedAction(),明确告知系统允许的拖放操作类型:void MapWidget::dragEnterEvent(QDragEnterEvent* event) { if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist")) { event->acceptProposedAction(); } else { event->ignore(); } }清理冗余代码
删除dropEvent中无关的rootObject()->setAcceptHoverEvents(true)语句,该设置与拖放功能无关。
完成以上修改后,拖放事件就能正常传递到dropEvent,地图也会显示允许放置的标志,实现预期的拖拽创建功能。
内容的提问来源于stack exchange,提问作者Jepessen
相关产品推荐
相关产品推荐

