一、需求说明

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

    1.服务器下载 VisualSVN 地址:http://subversion.apache.org/packages.html 2.然后下载TortoiseSVN客户端,如果要下中文语言包 也在这个页 ...

  2. Android开发(二十七)——android:layout_weight的真实含义

    android:layout_weight的真实含义是:一旦View设置了该属性(假设有效的情况下),那么该 View的宽度等于原有宽度(android:layout_width)加上剩余空间的占比! ...

  3. Asp.net Core WebApi 全局异常类

    通过全局异常类,所有程序中遇到的错误都会被拦截,并友好的返回结果. 1.自定义一个全局异常处理类中间件 using Microsoft.AspNetCore.Http; using Newtonsof ...

  4. Entity Framework 在Vs2012下Update Model From DataBase 失败的问题

    http://stackoverflow.com/questions/13054212/vs-2012-ef-5-0-update-model-from-database-not-picking-up ...

  5. 记录javascript 验证字符串布尔类型 及url 参数获取

    /^true$/i.test("false");false/^true$/i.test("true");true //获取请求参数的值 function Req ...

  6. DataInputStream类和RandomAccessFile类的使用方法

    // DataInputStream类实现了DataInput接口,要想从文件中读入二进制数据, // 你需要将DataInputStream与某个字节源相结合,例如FileInputStream / ...

  7. Carthage 安装和使用

    和Cocoapods相比各有利弊吧,具体对比参见: Carthage 初探:四大优势与四大劣势 第一步:如果没有安装Homebrew先安装 打开命令终端,直接输入以下命令回车 /usr/bin/rub ...

  8. 关于android中线性布局的layout_gravity属性

    当 android:orientation="vertical"  时, 只有水平方向的设置才起作用,垂直方向的设置不起作用.即:left,right,center_horizon ...

  9. 安装vs2013以后,链接数据库总是报内存损坏,无法写入的错误

    安装vs2013以后,链接数据库总是报内存损坏,无法写入的错误 这个错误几个月以前解决过一次,但是到又碰到的时候,竟然完全忘记当时怎么解决的了, 看来上了年纪记忆真是越来越不行了... 解决方案很简单 ...

  10. Sql Server:不允许 ASSIGNMENT 语句中包含 FOR XML 子句

    编写函数的时候遇到“不允许 ASSIGNMENT 语句中包含 FOR XML 子句”错误,开始以为数据库函数里不可以写 FOR XML 子句,仔细看了看总觉得这么写别扭索性改了一种写法就通过了. BE ...