一、需求說明
最近在搞視圖項的拖拽,也上網查了一些資料,好多的文檔都是一樣的,只是被不通的網站所收錄了(也有可能是被爬過去的,不明所以),不過也有一些文檔寫的不錯,不過就是太簡易,都是點睛之筆,總之功能還是勉強可以實現,加之比較零散,剛好我自己也因為這個需求寫了一個demo,因此我就把自己寫這個demo的過程分析給大家,希望能幫到有這個需求的小伙伴。
二、效果展示
如圖1是demo的效果展示,比較丑,如果加上優秀的qss,那必然能讓人眼前一亮。
圖1 ListWidget拖拽
三、實現思路
- 繼承QListWidget類,重寫其鼠標多拽時幾個虛方法,分別是:dragEnterEvent(鼠標拖拽進入),dragLeaveEvent(鼠標拖拽時離開),dragMoveEvent(拖拽時移動),dropEvent(鼠標釋放),(mousePressEvent)鼠標按下,mouseMoveEvent(鼠標移動)等。
- 鼠標按下時,記錄鼠標按下位置和鼠標點擊項
- 鼠標移動時構造一個QDrag對象,並且執行其exec方法,這個方法執行后,直到dropEvent觸發后,mouseMoveEvent方法才會被再次觸發,否則鼠標移動消息一直派發給dragMouseEvent。exec方法的之后,后續的鼠標事件都會在drag打頭的方法中回調。
- 拖拽期間,鼠標移動,並回調在dragMoveEvent方法中,可以在這個方法中修改鼠標狀態。維護一些變量,比如效果圖上跟隨鼠標一起移動的一張圖片和綠色的指示插入位置的一條線。
- 最后鼠標釋放時,判斷如果需要更新拖拽項位置,那么把原有項刪除,並構造新的項插入到目標位置。
四、代碼說明
1、首先來看幾個關鍵的類
MimeData:存儲拖拽時數據
ListItem:item項定制,展示自定義結構
DragList:視圖窗口
2、下面就直接上代碼,步驟對應第三小節的思路
a、記錄鼠標按下時信息
1 void DragList::mousePressEvent(QMouseEvent *event) 2 { 3 if (event->button() == Qt::LeftButton) 4 { 5 startPos = event->pos(); 6 dragItem = itemAt(event->pos()); 7 } 8 QListWidget::mousePressEvent(event); 9 }
b、鼠標移動時啟動QDrag,鼠標移動事件此后進入drag相關函數,直到dropEvent函數被調用
1 void DragList::mouseMoveEvent(QMouseEvent * event) 2 { 3 QListWidgetItem * item = itemAt(event->pos()); 4 if (dragItem == nullptr) 5 { 6 return; 7 } 8 9 m_Drag = new QDrag(this); 10 11 ListItem * itemWidget = nullptr; 12 if (itemWidget = ItemWidget(dragItem)) 13 { 14 MimeData * mimeData = new MimeData(itemWidget->GetData()); 15 m_Drag->setMimeData(mimeData); 16 } 17 18 m_Drag->setDragCursor(style()->standardPixmap(QStyle::SP_TitleBarMinButton), Qt::LinkAction); 19 m_Drag->setDragCursor(style()->standardPixmap(QStyle::SP_TitleBarMaxButton), Qt::MoveAction); 20 21 m_Drag->setHotSpot(QPoint(0, -m_Drag->pixmap().height() * 2)); 22 Qt::DropAction dropAction = m_Drag->exec(Qt::MoveAction | Qt::LinkAction, Qt::LinkAction); 23 if (dropAction == Qt::MoveAction) 24 { 25 if (itemWidget) 26 { 27 itemWidget->deleteLater(); 28 } 29 delete dragItem;//刪除原有的item,在dropEvent(QDropEvent *event)函數中插入item 30 } 31 32 QListWidget::mouseMoveEvent(event); 33 }
c、鼠標拖拽時移動
1 void DragList::dragMoveEvent(QDragMoveEvent * event) 2 { 3 DragList * source = (DragList *)((void *)(event->source())); 4 if (source && source == this) 5 { 6 QListWidgetItem * item = itemAt(event->pos()); 7 if (dragItem != nullptr && item != nullptr && m_Drag != nullptr) 8 { 9 if ((dragItem == item) != m_IsSelf) 10 { 11 m_IsSelf = dragItem == item; 12 13 if (m_IsSelf) 14 { 15 event->setDropAction(Qt::LinkAction); 16 } 17 else 18 { 19 event->setDropAction(Qt::MoveAction); 20 } 21 } 22 } 23 24 if (m_ShotPicture == nullptr) 25 { 26 InitShotLabel(); 27 } 28 29 if (ListItem * newWidget = ItemWidget(dragItem)) 30 { 31 m_ShotPicture->move(mapToGlobal(event->pos() - newWidget->mapFromParent(startPos))); 32 } 33 34 event->accept(); 35 } 36 }
d、鼠標釋放時處理拖拽結果
1 void DragList::dropEvent(QDropEvent * event) 2 { 3 if (m_ShotPicture) 4 { 5 m_ShotPicture->close(); 6 m_ShotPicture->deleteLater(); 7 m_ShotPicture = nullptr; 8 } 9 DragList * source = (DragList *)((void *)(event->source())); 10 if (source && source == this) 11 { 12 endPos = event->pos();//得到鼠標移動到的坐標 13 QListWidgetItem * itemRow = itemAt(endPos); //通過endPos獲取鼠標位置所在的行 14 if (itemRow == dragItem) 15 { 16 event->setDropAction(Qt::LinkAction); 17 } 18 else 19 { 20 int insertPos = row(itemRow); 21 if (ListItem * oldWidget = ItemWidget(itemRow)) 22 { 23 QPoint pos = oldWidget->mapFromParent(endPos); 24 if (oldWidget->size().height() / 2 < pos.y()) 25 { 26 insertPos += 1; 27 } 28 } 29 30 if (const MimeData * mimeData = dynamic_cast<const MimeData *>(event->mimeData())) 31 { 32 QListWidgetItem * newItem = new QListWidgetItem; 33 ListItem * itemWidget = new ListItem; 34 ItemData data = mimeData->GetData(); 35 itemWidget->SetData(data); 36 37 insertItem(insertPos, newItem); 38 setItemWidget(newItem, itemWidget); 39 } 40 event->setDropAction(Qt::MoveAction); 41 } 42 43 m_IsSelf = false; 44 event->accept(); 45 } 46 }
e、初始化跟隨鼠標移動的label,並把當前拖拽的窗口截圖設置給label
1 void DragList::InitShotLabel() 2 { 3 m_ShotPicture = new QLabel; 4 m_ShotPicture->setWindowOpacity(0.5); 5 m_ShotPicture->setWindowFlags(Qt::Popup); 6 m_ShotPicture->setAttribute(Qt::WA_TransparentForMouseEvents, true); 7 8 SetWindowLong((HWND)m_ShotPicture->winId(), GWL_EXSTYLE, GetWindowLong((HWND)m_ShotPicture->winId(), GWL_EXSTYLE) | 9 WS_EX_TRANSPARENT//忽略一切消息(WM_PAINT除外) 10 | WS_EX_LAYERED); 11 12 if (ListItem * oldWidget = ItemWidget(dragItem)) 13 { 14 m_ShotPicture->resize(oldWidget->size()); 15 m_ShotPicture->setPixmap(oldWidget->grab()); 16 } 17 m_ShotPicture->show(); 18 }
五、下載鏈接
六、相關文章
自定義拖放數據:這篇文章是講述怎么自定義QMimeData數據的,我使用的是其中第二個方法。
如果您覺得文章不錯,不妨給個
打賞,寫作不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!!