zoukankan      html  css  js  c++  java
  • Qt:Drag-Drop操作在QGraphicsView及Model/View框架下的实现

    最近使用到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,然后获取数据并处理。

    添加一张程序的截图

     

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

  • 相关阅读:
    [整理]ADB命令行学习笔记
    3、HTML的body内标签1
    2、HTML的head内标签
    1、HTML的本质以及在web中的作用
    3.11-3.15 HDFS HA
    3.9-3.10 分布式协作服务框架Zookeeper
    3.6-3.8 分布式环境启动、测试
    3.1-3.5 分布式部署hadoop2.x的准备和配置
    2.28 MapReduce在实际应用中常见的优化
    2.27 MapReduce Shuffle过程如何在Job中进行设置
  • 原文地址:https://www.cnblogs.com/helloworldhao/p/4365688.html
Copyright © 2011-2022 走看看