Qt之QAbstractItemView视图项拖拽(一)
一、需求说明
最近在搞视图项的拖拽,也上网查了一些资料,好多的文档都是一样的,只是被不通的网站所收录了(也有可能是被爬过去的,不明所以),不过也有一些文档写的不错,不过就是太简易,都是点睛之笔,总之功能还是勉强可以实现,加之比较零散,刚好我自己也因为这个需求写了一个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数据的,我使用的是其中第二个方法。
如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!