一、需求说明

最近在搞视图项的拖拽,也上网查了一些资料,好多的文档都是一样的,只是被不通的网站所收录了(也有可能是被爬过去的,不明所以),不过也有一些文档写的不错,不过就是太简易,都是点睛之笔,总之功能还是勉强可以实现,加之比较零散,刚好我自己也因为这个需求写了一个demo,因此我就把自己写这个demo的过程分析给大家,希望能帮到有这个需求的小伙伴。

二、效果展示

如图1是demo的效果展示,比较丑,如果加上优秀的qss,那必然能让人眼前一亮。

图1 ListWidget拖拽

三、实现思路

  1. 继承QListWidget类,重写其鼠标多拽时几个虚方法,分别是:dragEnterEvent(鼠标拖拽进入),dragLeaveEvent(鼠标拖拽时离开),dragMoveEvent(拖拽时移动),dropEvent(鼠标释放),(mousePressEvent)鼠标按下,mouseMoveEvent(鼠标移动)等。
  2. 鼠标按下时,记录鼠标按下位置和鼠标点击项
  3. 鼠标移动时构造一个QDrag对象,并且执行其exec方法,这个方法执行后,直到dropEvent触发后,mouseMoveEvent方法才会被再次触发,否则鼠标移动消息一直派发给dragMouseEvent。exec方法的之后,后续的鼠标事件都会在drag打头的方法中回调。
  4. 拖拽期间,鼠标移动,并回调在dragMoveEvent方法中,可以在这个方法中修改鼠标状态。维护一些变量,比如效果图上跟随鼠标一起移动的一张图片和绿色的指示插入位置的一条线。
  5. 最后鼠标释放时,判断如果需要更新拖拽项位置,那么把原有项删除,并构造新的项插入到目标位置。

四、代码说明

1、首先来看几个关键的类
    MimeData:存储拖拽时数据
    ListItem:item项定制,展示自定义结构
    DragList:视图窗口
2、下面就直接上代码,步骤对应第三小节的思路

a、记录鼠标按下时信息

 void DragList::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
startPos = event->pos();
dragItem = itemAt(event->pos());
}
QListWidget::mousePressEvent(event);
}

b、鼠标移动时启动QDrag,鼠标移动事件此后进入drag相关函数,直到dropEvent函数被调用

 void DragList::mouseMoveEvent(QMouseEvent * event)
{
QListWidgetItem * item = itemAt(event->pos());
if (dragItem == nullptr)
{
return;
} m_Drag = new QDrag(this); ListItem * itemWidget = nullptr;
if (itemWidget = ItemWidget(dragItem))
{
MimeData * mimeData = new MimeData(itemWidget->GetData());
m_Drag->setMimeData(mimeData);
} m_Drag->setDragCursor(style()->standardPixmap(QStyle::SP_TitleBarMinButton), Qt::LinkAction);
m_Drag->setDragCursor(style()->standardPixmap(QStyle::SP_TitleBarMaxButton), Qt::MoveAction); m_Drag->setHotSpot(QPoint(, -m_Drag->pixmap().height() * ));
Qt::DropAction dropAction = m_Drag->exec(Qt::MoveAction | Qt::LinkAction, Qt::LinkAction);
if (dropAction == Qt::MoveAction)
{
if (itemWidget)
{
itemWidget->deleteLater();
}
delete dragItem;//删除原有的item,在dropEvent(QDropEvent *event)函数中插入item
} QListWidget::mouseMoveEvent(event);
}

c、鼠标拖拽时移动

 void DragList::dragMoveEvent(QDragMoveEvent * event)
{
DragList * source = (DragList *)((void *)(event->source()));
if (source && source == this)
{
QListWidgetItem * item = itemAt(event->pos());
if (dragItem != nullptr && item != nullptr && m_Drag != nullptr)
{
if ((dragItem == item) != m_IsSelf)
{
m_IsSelf = dragItem == item; if (m_IsSelf)
{
event->setDropAction(Qt::LinkAction);
}
else
{
event->setDropAction(Qt::MoveAction);
}
}
} if (m_ShotPicture == nullptr)
{
InitShotLabel();
} if (ListItem * newWidget = ItemWidget(dragItem))
{
m_ShotPicture->move(mapToGlobal(event->pos() - newWidget->mapFromParent(startPos)));
} event->accept();
}
}

d、鼠标释放时处理拖拽结果

 void DragList::dropEvent(QDropEvent * event)
{
if (m_ShotPicture)
{
m_ShotPicture->close();
m_ShotPicture->deleteLater();
m_ShotPicture = nullptr;
}
DragList * source = (DragList *)((void *)(event->source()));
if (source && source == this)
{
endPos = event->pos();//得到鼠标移动到的坐标
QListWidgetItem * itemRow = itemAt(endPos); //通过endPos获取鼠标位置所在的行
if (itemRow == dragItem)
{
event->setDropAction(Qt::LinkAction);
}
else
{
int insertPos = row(itemRow);
if (ListItem * oldWidget = ItemWidget(itemRow))
{
QPoint pos = oldWidget->mapFromParent(endPos);
if (oldWidget->size().height() / < pos.y())
{
insertPos += ;
}
} if (const MimeData * mimeData = dynamic_cast<const MimeData *>(event->mimeData()))
{
QListWidgetItem * newItem = new QListWidgetItem;
ListItem * itemWidget = new ListItem;
ItemData data = mimeData->GetData();
itemWidget->SetData(data); insertItem(insertPos, newItem);
setItemWidget(newItem, itemWidget);
}
event->setDropAction(Qt::MoveAction);
} m_IsSelf = false;
event->accept();
}
}

e、初始化跟随鼠标移动的label,并把当前拖拽的窗口截图设置给label

 void DragList::InitShotLabel()
{
m_ShotPicture = new QLabel;
m_ShotPicture->setWindowOpacity(0.5);
m_ShotPicture->setWindowFlags(Qt::Popup);
m_ShotPicture->setAttribute(Qt::WA_TransparentForMouseEvents, true); SetWindowLong((HWND)m_ShotPicture->winId(), GWL_EXSTYLE, GetWindowLong((HWND)m_ShotPicture->winId(), GWL_EXSTYLE) |
WS_EX_TRANSPARENT//忽略一切消息(WM_PAINT除外)
| WS_EX_LAYERED); if (ListItem * oldWidget = ItemWidget(dragItem))
{
m_ShotPicture->resize(oldWidget->size());
m_ShotPicture->setPixmap(oldWidget->grab());
}
m_ShotPicture->show();
}

五、下载链接

  Qt之QAbstractItemView视图项拖拽

六、相关文章

自定义拖放数据:这篇文章是讲述怎么自定义QMimeData数据的,我使用的是其中第二个方法。

  Qt之QAbstractItemView选择无焦点

如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!! 

 

很重要--转载声明

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。

Qt之QAbstractItemView视图项拖拽(一)的更多相关文章

  1. Qt之QAbstractItemView视图项拖拽(二)

    一.需求说明 上一篇文章Qt之QAbstractItemView视图项拖拽(一)讲述了实现QAbstractItemView视图项拖拽的一种方式,是基于QDrag实现的,这个类是qt自己封装好了的,所 ...

  2. Qt图形视图体系结构示例解析(视图、拖拽、动画)

    本博的示例来自与QT Example:C:\Qt\Qt5.9.3\Examples\Qt-5.9.3\widgets\graphicsview\dragdroprobot 将通过分析示例完成主要功能: ...

  3. Qt实现不同Treewidget之间拖拽

    拖拽是编程中经常要用到的,我这里主要是实习了Treewidget之间直接拖拽Item,按下Ctrl键的话是copy,不按Ctrl则是Move.以下是实现代码 class TreeItemMimeDat ...

  4. Qt窗口添加鼠标移动拖拽事件

    1. .h文件中添加 private:    QPoint dragPosition; 2. 在cpp文件中重写鼠标点击和拖拽函数 void ShapeWidget::mousePressEvent( ...

  5. QT笔记之自定义窗口拖拽移动

    1.QT自定义标题栏,拖拽标题栏移动窗口(只能拖拽标题,其他位置无法拖拽) 方法一: 转载:http://blog.sina.com.cn/s/blog_4ba5b45e0102e83h.html . ...

  6. ztree插件的使用及列表项拖拽的实现(jQuery)+异步加载节点数据

    为了实现如图所示的树状结构图,并使列表项可拖动到盒子里,研究了ztree这个插件的使用,并仔细研究了列表项的拖动事件.完成了预期需求,对jQuery的运用得到了提高.这个插件的功能非常强大,除了基本的 ...

  7. win32 sdk树形控件的项拖拽实现

    本课中,我们将学习如何使用树型视图控件.另外还要学习如何在树型视图中完成拖-拉动作,以及如何使用图象列表. 理论: 树型视图是一种特别的窗口,我们可以使用它一目了然地表示某种层次关系.譬如象在资源管理 ...

  8. qt 拖拽 修改大小(二)

    最近项目需要实现windows下橡皮筋的效果,所以对此做了一些了解,特此记录. 首先windows系统是支持橡皮筋效果的,需要使用win32方 法:SystemParametersInfo(SPI_S ...

  9. Qt之QAbstractItemView右键菜单

    一.功能概述 说起右键菜单,之前Qt之自定义QLineEdit右键菜单这篇文章中我已经讲述过3种右键菜单的实现方式,今儿也是在啰嗦一下,针对QListWidget类在定制一下右键菜单,我使用的具体方式 ...

随机推荐

  1. 使用Installutil安装系统服务方法

    系统必须装有.net Framework2.0然后点击开始-运行输入以下指令即可完成相应操作安装服务:C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Ins ...

  2. 如何开发Domino中的WebService

    在domino中写webservice可以使用LotusScript,也可以使用java,由于LotusScript API提供的功能多数都是操作domino数据库中文档的,在web service中 ...

  3. showmessage函数里

    首先说一下,漏洞是t00ls核心群传出去的,xhming先去读的,然后我后来读的,读出来的都是代码执行,1月5日夜里11点多钟,在核心群的黑客们的要求下,xhming给了个poc,我给了个exp,确实 ...

  4. Java 多线程(1)-Thread和Runnable

    一提到Java多线程,首先想到的是Thread继承和Runnable的接口实现 Thread继承 public class MyThread extends Thread { public void ...

  5. 趣拍SDK接入问题Android

    Android接入趣拍问题. 大部分android开发者第一次下载SDK后,特别是导入到eclipse后,可以运行工程,但点击app中的record没反映,每次点击record按钮 会出现如下log. ...

  6. blueImp/jQuery file upload 的正确用法(限制上传大小和文件类型)

    这个插件太出名了,几乎能完成所有能想象的到的效果,包括进度条.拖拽.甚至现在已经完美支持图片视频等的处理,三个字形容就是屌爆了.最近在做上传这一部分,发现网上对于上传文件大小的限制和类型检测等的方法都 ...

  7. config中自定义配置

    1. 定义自己的KeyValue <section name="TestKeyValue" type="System.Configuration.NameValue ...

  8. Spring MVC 事务配置

    Spring MVC事务配置 要了解事务配置的所有方法,请看一下<Spring事务配置的5种方法> 本文介绍两种配置方法: 一.      XML,使用tx标签配置拦截器实现事务 一.   ...

  9. Spark和hadoop的关系

    1. Spark VSHadoop有哪些异同点? Hadoop:分布式批处理计算,强调批处理,常用于数据挖掘和数据分析. Spark:是一个基于内存计算的开源的集群计算系统,目的是让数据分析更加快速, ...

  10. [LeetCode] Range Sum Query 2D - Immutable

    Very similar to Range Sum Query - Immutable, but we now need to compute a 2d accunulated-sum. In fac ...