最近使用到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,然后獲取數據並處理。
添加一張程序的截圖

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