最近使用到Qt的Drag Drop功能,结合自己的例子写下来给大家分享一下。实现从QTreeView拖动Node到QGraphicsView上,以及QGraphicsView上item之间的拖动。

先来说Model/View中的实现

1.Model/View要实现Drag Drop操作,首先需要为View设置DragDropMode属性。

enum DragDropMode
{ NoDragDrop, DragOnly, DropOnly, DragDrop, InternalMove }

以上是View支持的所有Mode Type,View默认是NoDragDrop,即默认不支持拖拽。

m_treeView->setDragDropMode(QAbstractItemView::DragOnly);

我只需要TreeView的Drag操作,所以设置成DragOnly,可以依据自己需要实现的功能来设置。

2.Drag and Drop是两个操作,独立分开的。

Drag 实现需要的步骤:第一步,在对应的Model的flags函数中,允许drag的item返回Qt::ItemIsDragEnabled。

Qt::ItemFlags NetlistModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
flags = flags | Qt::ItemIsDragEnabled;
return flags;
}

第二步,Model中需要实现mimeData函数,该函数是封装Drag数据,数据由Drop方接收处理。该步是处理传递的数据。

QMimeData *NetlistModel::mimeData(const QModelIndexList &indexes) const
{
if (indexes.count() <= 0)
{//If the list of indexes is empty, or there are no supported MIME types,
//0 is returned rather than a serialized empty list.
return 0;
} //QMap<int, QStringList> nodeData;
//QStringList listName;
// foreach(QModelIndex index, indexes)
// {
// TreeNode* node = nodeFromIndex(index);
// } QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly); TreeNode* node = nodeFromIndex(indexes.at(0));
NlsData::ConItem* conItem = static_cast<NlsData::ConItem*>(node->data());
if (conItem)
{
dataStream << node->type() << conItem->name();
} QMimeData *data = new QMimeData;
data->setData("nlsdata/items", itemData);//key值需要与Drop中一致
return data;
}

QTreeView我只需要单个拖拽,所以数据处理是单个index,如果多个拖拽,可以将数据封装成List or Map,注意QDataStream只可以放Qt的基本数据类型,自定义的struct其中也只能是基本数据类型,不能放指针。

3.对于Drop操作,步骤也相似,第一步flags函数中返回Qt::ItemIsDropEnabled;第二步需要实现mimeTypes函数,这个函数要返回当前数据模型允许接收的数据类型列表,它会在Drag操作过程中被调用,如果Drag操作所包含的对象(mimeData方法返回的数据对象)没有相关类型的数据,就不允许执行Drop操作;第三步实现dropMimeData方法。这个方法主要在drop操作后解析数据并添加到当前模型的合适位置。因为我在QTreeView不允许Drop操作,所以就不贴代码了。

接下来是QGraphicsView中的实现。

1.设置QGraphicsView的DragMode。关于DragMode,This property holds the behavior for dragging the mouse over the scene while the left mouse button is pressed.

在View的构造函数中添加

setDragMode(ScrollHandDrag);

不过该函数似乎只设置了Drag时的鼠标样式,因为我的Drag Drop都是在QGraphicsItem中实现的。

2.在QGraphicsItem的构造函数中设置setAcceptDrops(true),允许接收Drop。

if (!border)
{
  setFlags(ItemIsSelectable);
  setAcceptsHoverEvents(true);
  setAcceptDrops(true);
}

当然,为需要的Item设置需要的。

3.实现Item的一下事件

void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
void dropEvent(QGraphicsSceneDragDropEvent *event);//处理drop
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);//添加QDrag

dragEnterEvent中,如果有需要的数据,event->setAccepted(true),接收drag事件

void GPkgCellItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
if (event->mimeData()->hasFormat("nlsdata/items"))
{
event->setAccepted(true);
m_bOnDrag = true;//painter函数中使用,绘制不同的样式
update();
}
else
{
event->setAccepted(false);
}
}

dragLeaveEvent

void GPkgCellItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
Q_UNUSED(event);
m_bOnDrag = false;
update();
}

dropEvent中需要处理mimeData,获得drag传过来的数据。

void GPkgCellItem::dropEvent(QGraphicsSceneDragDropEvent *event)
{
m_bOnDrag = false;
if (event->mimeData()->hasFormat("nlsdata/items"))
{
QByteArray itemData = event->mimeData()->data("nlsdata/items");
QDataStream dataStream(&itemData, QIODevice::ReadOnly);
dataStream >> m_dragType >> m_dragName;
setAcceptDrops(false);
}
update();
}

实现QGraphicsItem之间的Drag,需要在mouseMoveEvent中添加QDrag动作,该Drag也在dropEvent中接收。

void GPkgCellItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton))
.length() < QApplication::startDragDistance())
{//触发需满足move事件最小距离(The default value is 4 pixels.)
return;
} if (m_dragName.isEmpty())
{
return;
} QMimeData *mimeData = new QMimeData;
QByteArray exData;
QDataStream dataStream (&exData, QIODevice::WriteOnly);
dataStream << m_dragType << m_dragName;
mimeData->setData("nlsdata/items", exData);
QDrag *drag = new QDrag (event->widget());
drag->setMimeData(mimeData);
if (drag->exec(Qt::MoveAction) == Qt::MoveAction)
{//drag动作完成、Drop处理之后的动作,在dropEvent调用之后被调用,相当于回调函数,可以把数据清理之类的放在此处处理。
m_dragName = "";
m_dragType = 0;
setAcceptDrops(true);//随时更新状态,是否允许drop
update();
}
}

Drag-Drop的机制就是在开始拖动鼠标时启动QDrag并封装数据,然后drag到某个位置松开鼠标触发Drop,然后获取数据并处理。

添加一张程序的截图

记录,成长过程的每一步。。。

Qt:Drag-Drop操作在QGraphicsView及Model/View框架下的实现的更多相关文章

  1. WPF实现Drag/Drop操作

    原文:WPF实现Drag/Drop操作 有时候我们方便用户操作,总会把一下Copy/Paste 或者 input操作转换为Drag/Drop, WPF 跟之前WinForm 一样提供了一些实现方式方便 ...

  2. Qt Model View 框架

    Model-View及Qt实现 Model-View-Controller架构最早出现在SmallTalk语言中,至今出现了很多变体. Model是负责维护数据(如管理数据库),View负责显示与用户 ...

  3. 第15.27节 PyQt(Python+Qt)入门学习:Model/View架构中的便利类QTreeWidget详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.引言 树部件(Tree Widget)是Qt Designer中 Item Widgets(It ...

  4. 第15.22节 PyQt(Python+Qt)入门学习:Model/View架构详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.简介 在PyQt和Qt中,Model/View架构是图形界面开发时用于管理数据和界面展现方式的关 ...

  5. PyQt(Python+Qt)学习随笔:model/view架构中的排序和代理模型QSortFilterProxyModel

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.概述 在Model/View体系架构中,有两种方法可以进行排序:选择哪种方法取决于底层模型. 如 ...

  6. qt model/view 架构基础介绍之QListWidget

    # -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' from PyQt4.QtGui import  * from Py ...

  7. 第二十二章、 Model/View便利类树型部件QTreeWidget

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.引言 树部件(Tree Widget)是Qt Designer中 Item Widgets(It ...

  8. Model/View开发小结

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 Model/View开发是PyQt和Qt中重要的框架之一,老猿认为另外两个就是信号槽机制和事件机制, ...

  9. PyQt学习随笔:Model/View架构中的Delegate(委托)

    不同于MVC模式,Model/View设计并不包含用于处理与用户交互的完全独立的部件, 没有将用户交互部分完全分离.一般地,视图负责把模型数据显示给用户,以及处理用户的输入.但是,对于某些特殊要求(比 ...

随机推荐

  1. EF 如何更新少量字段

    EF更新少量字段需要解决两个问题 1.动态的将需要更新的字段提取出来 2.将提取出来的字段设为更新状态 通常更新的时候,都是根据条件将实体取出来,然后赋值字段,最后更新整个实体,所以在方法上看似是更新 ...

  2. https 单向认证和双向认证配置

    HTTPS 是我们开发中经常用到的通信加密技术,能有效保护我们网络访问中的安全,本文主要讲解单向 和 双向 https 的配置.关于https 的实现原理在这里我就不赘述了,附上阮一峰老师的关于htt ...

  3. easyUI的Dialog和Windows框的应用

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...

  4. usr类库的使用(一般用在第三方类库使用系统库报错头文件找不到时)

    第三方Html解析类库Hpple,在导入框架libxml2.2.dylib后,XCode仍然找不到<libxml/tree.h>. 1 .项目 -Targets 中的 Build P ha ...

  5. selenium定位元素(本内容从https://my.oschina.net/flashsword/blog/147334处转载)

    注明:本内容从https://my.oschina.net/flashsword/blog/147334处转载. 在使用selenium webdriver进行元素定位时,通常使用findElemen ...

  6. Navigator

      Navigator   这是一个简单的例子,用Navigator来跳转页面,页面之间传递参数 (代码是ES6语法写的): import React from 'react'; import { V ...

  7. AS 重装系统之后配置

    重新安装了win7 系统,一起的AS 放在其他盘里 1.重新安装java sdk 配置java 环境. 2,从新配置AS 工作界面及各种配置 3,重新安装genymotion 并在as 中配置 出现 ...

  8. 微信小程序-地图组件

    map 地图. 标记点 标记点用于在地图上显示标记的位置,不能自定义图标和样式 覆盖物 覆盖物用于在地图上显示自定义图标,可自定义图标和样式 地图组件的经纬度必填, 如果不填经纬度则默认值是北京的经纬 ...

  9. ucos中的三种临界区管理机制

    熟悉ucos,或者读过Jean.J.Labrosse写过的ucos书籍的人,一定会知道ucos中著名的临界区管理宏:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(). 同样 ...

  10. IE7中绝对定位元素之间的遮盖问题

    由于页面复杂就不上现实例子,举例说明. 两个同层级<div>元素,都设置了 position: relative;相对定位. 他们的内部的元素(不管什么元素了)都设置了position: ...