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,然后獲取數據並處理。

添加一張程序的截圖

 

 

記錄,成長過程的每一步。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM