一、需求说明
上一篇文章Qt之QAbstractItemView视图项拖拽(一)讲述了实现QAbstractItemView视图项拖拽的一种方式,是基于QDrag实现的,这个类是qt自己封装好了的,所以可定制性也就没有了那么强,最明显的是,这个类在执行exec方法后,mouse系列的回调接口就被阻塞了,随之而来的问题就是拖拽时item项没有了hover特性,为了解决这个问题,我们就不能使用QDrag类来实现拖拽了,这也是这篇文章我要讲述的内容。
二、效果展示
如图1是demo的效果展示,比较丑,如果加上优秀的qss,那必然能让人眼前一亮
图1 ListWidget拖拽
![](https://i-blog.csdnimg.cn/blog_migrate/30a9503e0cbc7ba6160ae8131d387a6a.gif)
三、实现思路
- 继承QListWidget类,重写其鼠标多拽时几个虚方法,分别是mousePressEvent(鼠标按下),mouseMoveEvent(鼠标移动),mouseReleaseEvent(鼠标弹起)等,当然还包括一些辅助的回调方法enterEvent和leaveEvent。
- 鼠标按下时,记录鼠标按下位置和鼠标点击项
- 鼠标移动时移动插入项标示和item项快照位置,并修改鼠标形状
- 最后鼠标释放时,判断如果需要更新拖拽项位置,那么把原有项删除,并构造新的项插入到目标位置
-
上边的几个步骤描述都是在mouse系列的回到接口中发生的,再也没有QDrag的事儿啦。当然这个mouse方法中需要做一些鼠标状态维护等。
四、代码说明
1、重要的类和上一篇文章中的一样,忘记的小伙伴可以到上一篇文章查看,或者猛戳Qt之QAbstractItemView视图项拖拽(一)
2、下面就直接上代码
a、记录鼠标按下时信息
void DragList::mousePressEvent(QMouseEvent * event)
{
if (event->button() == Qt::LeftButton)
{
m_LeftPress = true;
startPos = event->pos();
dragItem = itemAt(event->pos());
}
QListWidget::mousePressEvent(event);
}
b、鼠标移动时维护鼠标状态、快照位置和插入表示位置
void DragList::mouseMoveEvent(QMouseEvent * event)
{
QListWidgetItem * item = itemAt(event->pos());
if (dragItem == nullptr)
{
dragItem = itemAt(event->pos());
}
if (m_ShotPicture == nullptr)
{
InitShotLabel();
}
if (m_ShotLine == nullptr)
{
InitShotLine();
}
QRect rect = visualItemRect(dragItem);
if (ListItem * hoverWidget = ItemWidget(item))
{
QRect hoverRect = visualItemRect(item);
QPoint pos = hoverWidget->mapFromParent(event->pos());
if (hoverRect.size().height() / 2 < pos.y())
{
m_ShotLine->move(mapToGlobal(QPoint(2, hoverRect.y() + hoverRect.height() + 1)));
}
else
{
m_ShotLine->move(mapToGlobal(QPoint(2, hoverRect.y() + 1)));
}
m_ShotLine->setVisible(hoverRect.contains(event->pos()));
}
if (ListItem * newWidget = ItemWidget(dragItem))
{
m_ShotPicture->move(mapToGlobal(event->pos() - newWidget->mapFromParent(startPos)));
if (rect.contains(event->pos()) || event->pos().isNull())
{
setCursor(Qt::ForbiddenCursor);
}
else
{
setCursor(Qt::ArrowCursor);
}
if (m_ShotPicture->isHidden())
{
m_ShotPicture->show();
}
}
// QListWidget::mouseMoveEvent(event);
}
c、鼠标释放时处理拖拽结果
void DragList::mouseReleaseEvent(QMouseEvent * event)
{
if (event->button() == Qt::LeftButton)
{
m_LeftPress = false;
if (m_ShotPicture)
{
m_ShotPicture->close();
m_ShotPicture->deleteLater();
m_ShotPicture = nullptr;
}
if (m_ShotLine)
{
m_ShotLine->close();
m_ShotLine->deleteLater();
m_ShotLine = nullptr;
}
MouseRelease(event);
}
setCursor(Qt::ArrowCursor);
QListWidget::mouseReleaseEvent(event);
}
d、初始化跟随鼠标移动的快照,并把当前拖拽的窗口截图设置给快照
void DragList::InitShotLabel()
{
m_ShotPicture = new QLabel;
m_ShotPicture->setWindowOpacity(0.95);
m_ShotPicture->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);
m_ShotPicture->setAttribute(Qt::WA_TransparentForMouseEvents, true);
if (ListItem * oldWidget = ItemWidget(dragItem))
{
m_ShotPicture->setPixmap(oldWidget->grab());
m_ShotPicture->resize(visualItemRect(dragItem).size());
}
m_ShotPicture->show();
}
e、初始化鼠标插入位置标示
void DragList::InitShotLine()
{
m_ShotLine = new QLabel;
m_ShotLine->setObjectName(QStringLiteral("ShotLine"));
m_ShotLine->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);
m_ShotLine->setAttribute(Qt::WA_TransparentForMouseEvents, true);
m_ShotLine->setStyleSheet("QLabel#ShotLine{background:green;}");//用图片代替
if (ListItem * oldWidget = ItemWidget(dragItem))
{
// m_ShotLine->setPixmap(oldWidget->grab());
m_ShotLine->resize(visualItemRect(dragItem).size().width(), 2);
}
m_ShotLine->show();
}
f、鼠标弹起具体处理函数
void DragList::MouseRelease(QMouseEvent * event)
{
QListWidgetItem * item = itemAt(event->pos());
if (item == nullptr || item == dragItem)
{
return;
}
int insertPos = row(item);
if (ListItem * oldWidget = ItemWidget(item))
{
QPoint pos = oldWidget->mapFromParent(event->pos());
if (oldWidget->size().height() / 2 < pos.y())
{
insertPos += 1;
}
}
if (dragItem)
{
if (ListItem * oldWidget = ItemWidget(dragItem))
{
QListWidgetItem * newItem = new QListWidgetItem;
ListItem * itemWidget = new ListItem;
itemWidget->SetData(oldWidget->GetData());
insertItem(insertPos, newItem);
setItemWidget(newItem, itemWidget);
setCurrentItem(newItem);
oldWidget->deleteLater();
}
dragItem = takeItem(row(dragItem));
if (dragItem)
{
delete dragItem;
dragItem = nullptr;
}
}
}
五、下载链接
六、相关文章
自定义拖放数据:这篇文章是讲述怎么自定义QMimeData数据的,我使用的是其中第二个方法。
如果您觉得文章不错,不妨给个
打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!
![]() | ![]() |
很重要–转载声明
-
本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
-
如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。