一、需求说明
上一篇文章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视图项拖拽(一)