一、需求說明???
??? 上一篇文章Qt之QAbstractItemView視圖項拖拽(一)講述了實現QAbstractItemView視圖項拖拽的一種方式,是基于QDrag實現的,這個類是qt自己封裝好了的,所以可定制性也就沒有了那么強,最明顯的是,這個類在執行exec方法后,mouse系列的回調接口就被阻塞了,隨之而來的問題就是拖拽時item項沒有了hover特性,為了解決這個問題,我們就不能使用QDrag類來實現拖拽了,這也是這篇文章我要講述的內容。
二、效果展示 ?
??? 如圖1是demo的效果展示,比較丑,如果加上優秀的qss,那必然能讓人眼前一亮
圖1 ListWidget拖拽
三、實現思路
- 繼承QListWidget類,重寫其鼠標多拽時幾個虛方法,分別是mousePressEvent(鼠標按下),mouseMoveEvent(鼠標移動),mouseReleaseEvent(鼠標彈起)等,當然還包括一些輔助的回調方法enterEvent和leaveEvent。
- 鼠標按下時,記錄鼠標按下位置和鼠標點擊項
- 鼠標移動時移動插入項標示和item項快照位置,并修改鼠標形狀
- 最后鼠標釋放時,判斷如果需要更新拖拽項位置,那么把原有項刪除,并構造新的項插入到目標位置
?? ?? 上邊的幾個步驟描述都是在mouse系列的回到接口中發生的,再也沒有QDrag的事兒啦。當然這個mouse方法中需要做一些鼠標狀態維護等。
四、代碼說明
??? 1、重要的類和上一篇文章中的一樣,忘記的小伙伴可以到上一篇文章查看,或者猛戳Qt之QAbstractItemView視圖項拖拽(一)
?? ?2、下面就直接上代碼
?? ?a、記錄鼠標按下時信息
1 void DragList::mousePressEvent(QMouseEvent * event) 2 { 3 if (event->button() == Qt::LeftButton) 4 { 5 m_LeftPress = true; 6 startPos = event->pos(); 7 dragItem = itemAt(event->pos()); 8 } 9 10 QListWidget::mousePressEvent(event); 11 }
??? b、鼠標移動時維護鼠標狀態、快照位置和插入表示位置
1 void DragList::mouseMoveEvent(QMouseEvent * event) 2 { 3 QListWidgetItem * item = itemAt(event->pos()); 4 if (dragItem == nullptr) 5 { 6 dragItem = itemAt(event->pos()); 7 } 8 9 if (m_ShotPicture == nullptr) 10 { 11 InitShotLabel(); 12 } 13 if (m_ShotLine == nullptr) 14 { 15 InitShotLine(); 16 } 17 18 QRect rect = visualItemRect(dragItem); 19 if (ListItem * hoverWidget = ItemWidget(item)) 20 { 21 QRect hoverRect = visualItemRect(item); 22 QPoint pos = hoverWidget->mapFromParent(event->pos()); 23 if (hoverRect.size().height() / 2 < pos.y()) 24 { 25 m_ShotLine->move(mapToGlobal(QPoint(2, hoverRect.y() + hoverRect.height() + 1))); 26 } 27 else 28 { 29 m_ShotLine->move(mapToGlobal(QPoint(2, hoverRect.y() + 1))); 30 } 31 32 m_ShotLine->setVisible(hoverRect.contains(event->pos())); 33 } 34 35 if (ListItem * newWidget = ItemWidget(dragItem)) 36 { 37 m_ShotPicture->move(mapToGlobal(event->pos() - newWidget->mapFromParent(startPos))); 38 if (rect.contains(event->pos()) || event->pos().isNull()) 39 { 40 setCursor(Qt::ForbiddenCursor); 41 } 42 else 43 { 44 setCursor(Qt::ArrowCursor); 45 } 46 if (m_ShotPicture->isHidden()) 47 { 48 m_ShotPicture->show(); 49 } 50 } 51 52 53 // QListWidget::mouseMoveEvent(event); 54 }
??? c、鼠標釋放時處理拖拽結果
1 void DragList::mouseReleaseEvent(QMouseEvent * event) 2 { 3 if (event->button() == Qt::LeftButton) 4 { 5 m_LeftPress = false; 6 if (m_ShotPicture) 7 { 8 m_ShotPicture->close(); 9 m_ShotPicture->deleteLater(); 10 m_ShotPicture = nullptr; 11 } 12 if (m_ShotLine) 13 { 14 m_ShotLine->close(); 15 m_ShotLine->deleteLater(); 16 m_ShotLine = nullptr; 17 } 18 MouseRelease(event); 19 } 20 21 setCursor(Qt::ArrowCursor); 22 23 QListWidget::mouseReleaseEvent(event); 24 }
??? d、初始化跟隨鼠標移動的快照,并把當前拖拽的窗口截圖設置給快照
1 void DragList::InitShotLabel() 2 { 3 m_ShotPicture = new QLabel; 4 m_ShotPicture->setWindowOpacity(0.95); 5 m_ShotPicture->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint); 6 m_ShotPicture->setAttribute(Qt::WA_TransparentForMouseEvents, true); 7 8 if (ListItem * oldWidget = ItemWidget(dragItem)) 9 { 10 m_ShotPicture->setPixmap(oldWidget->grab()); 11 m_ShotPicture->resize(visualItemRect(dragItem).size()); 12 } 13 m_ShotPicture->show(); 14 }
??? e、初始化鼠標插入位置標示
1 void DragList::InitShotLine() 2 { 3 m_ShotLine = new QLabel; 4 m_ShotLine->setObjectName(QStringLiteral("ShotLine")); 5 m_ShotLine->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint); 6 m_ShotLine->setAttribute(Qt::WA_TransparentForMouseEvents, true); 7 m_ShotLine->setStyleSheet("QLabel#ShotLine{background:green;}");//用圖片代替 8 9 if (ListItem * oldWidget = ItemWidget(dragItem)) 10 { 11 // m_ShotLine->setPixmap(oldWidget->grab()); 12 m_ShotLine->resize(visualItemRect(dragItem).size().width(), 2); 13 } 14 m_ShotLine->show(); 15 }
??? f、鼠標彈起具體處理函數
1 void DragList::MouseRelease(QMouseEvent * event) 2 { 3 QListWidgetItem * item = itemAt(event->pos()); 4 if (item == nullptr || item == dragItem) 5 { 6 return; 7 } 8 9 int insertPos = row(item); 10 if (ListItem * oldWidget = ItemWidget(item)) 11 { 12 QPoint pos = oldWidget->mapFromParent(event->pos()); 13 if (oldWidget->size().height() / 2 < pos.y()) 14 { 15 insertPos += 1; 16 } 17 } 18 19 if (dragItem) 20 { 21 if (ListItem * oldWidget = ItemWidget(dragItem)) 22 { 23 QListWidgetItem * newItem = new QListWidgetItem; 24 ListItem * itemWidget = new ListItem; 25 itemWidget->SetData(oldWidget->GetData()); 26 27 insertItem(insertPos, newItem); 28 setItemWidget(newItem, itemWidget); 29 30 setCurrentItem(newItem); 31 32 oldWidget->deleteLater(); 33 } 34 35 dragItem = takeItem(row(dragItem)); 36 if (dragItem) 37 { 38 delete dragItem; 39 dragItem = nullptr; 40 } 41 } 42 }
五、下載鏈接
?? Qt之QAbstractItemView視圖項拖拽2?
六、相關文章
自定義拖放數據:這篇文章是講述怎么自定義QMimeData數據的,我使用的是其中第二個方法。
Qt之QAbstractItemView視圖項拖拽(一)