You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现从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层做调整:

  1. 修改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();
            }
        }
    }
    
  2. 调整C++层的dragEnterEvent,使用正确的接受方式
    将dragEnterEvent中的event->accept()替换为event->acceptProposedAction(),明确告知系统允许的拖放操作类型:

    void MapWidget::dragEnterEvent(QDragEnterEvent* event) {
      if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist")) {
        event->acceptProposedAction();
      }
      else {
        event->ignore();
      }
    }
    
  3. 清理冗余代码
    删除dropEvent中无关的rootObject()->setAcceptHoverEvents(true)语句,该设置与拖放功能无关。

完成以上修改后,拖放事件就能正常传递到dropEvent,地图也会显示允许放置的标志,实现预期的拖拽创建功能。

内容的提问来源于stack exchange,提问作者Jepessen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 23:06:34