一、需求说明

最近在搞视图项的拖拽,也上网查了一些资料,好多的文档都是一样的,只是被不通的网站所收录了(也有可能是被爬过去的,不明所以),不过也有一些文档写的不错,不过就是太简易,都是点睛之笔,总之功能还是勉强可以实现,加之比较零散,刚好我自己也因为这个需求写了一个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. 【转】Java之WeakReference与SoftReference使用讲解

    Java 2 平台引入了 java.lang.ref 包,其中包括的类可以让您引用对象,而不将它们留在内存中.这些类还提供了与垃圾收集器(garbage collector)之间有限的交互. 1.先“ ...

  2. 从Tmux 转到GNU Screen

    网上很多地方都说Tmux比GNU Screen要好用,不过无意间看到这篇Switching from tmux to GNU Screen之后,我发现GNU Screen的窗口/区域概念更好,至少是更 ...

  3. Changing the type of a property with EF Code First

    The smartest way is probably to not alter types. If you need to do this, I'd suggest you to do the f ...

  4. 浅谈html5网页内嵌视频

    更好的阅读体验:浅谈html5网页内嵌视频 如今在这个特殊的时代下:flash将死未死,微软和IE的历史问题,html5标准未定,苹果和谷歌的闭源和开源之争,移动互联网的大势所趋,浏览器各自为战... ...

  5. 学习之路三十六:SQL知识总结 - [游标||字符串分割]

    好久没有写文章了,今天把前不久项目用到的SQL知识总结一下. 一丶字符串分割 SQL内置函数中是没有Split分割函数的,所以需要自己去实现,不多说,上代码: )) RETURNS @result T ...

  6. Kafka - SQL 代码实现

    1.概述 上次给大家分享了关于 Kafka SQL 的实现思路,这次给大家分享如何实现 Kafka SQL.要实现 Kafka SQL,在上一篇<Kafka - SQL 引擎分享>中分享了 ...

  7. android 监听软键盘的收起与打开

    参考: http://toughcoder.net/blog/2015/10/09/android-trick-detect-soft-keyboard-show-slash-hide/ packag ...

  8. typeof instanceof 之间的区别总结

        typeof   它返回值是一个字符串,该字符串说明运算数的类型. a=1; b=true; c="c"; d=function(){ console.log(" ...

  9. jquerymobile标签-属性手册

    Button data-role="button"   data-corners true | false data-icon home | delete | plus | arr ...

  10. 如果你遇到,在IntelliJ IDEA里Ctrl+Alt+方向键用不了

    在idea中使用ctrl+b跟踪进入函数之后,每次返回都不知道用什么快捷键,在idea中使用ctrl+alt+方向键首先会出现与win7屏幕方向的快捷键冲突,右键桌面,选择图形属性,将win7的快捷键 ...