qt自定义窗口在拖动过程中出现抖动且拖动后位置看上去不对

2024-04-08 13:20

本文主要是介绍qt自定义窗口在拖动过程中出现抖动且拖动后位置看上去不对,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

自定义窗口拖动

  • 引言
  • 开发环境
  • 关键性代码
  • 运行结果
  • 原因分析
  • 改进代码
  • 运行结果
  • globalPos()
  • globalPosition()
  • 再次修改代码
  • 运行结果
  • 区别

引言

本文旨在一个问题的记录:自定义窗口拖动的过程中,窗口不能很好的跟随鼠标移动,此外会出现窗口拖动时抖动。
下面是上文描述的效果:

拖动时窗口抖动


本文针对上面的问题,找出解决方法,且说明为什么。

开发环境

使用QtCreator开发。其相关的Qt库使用Qt6.6.0。
在这里插入图片描述

关键性代码

这里只给出自定义窗口拖动功能需要的部分关键代码。

    //窗口拖动相关变量声明QPointF m_pressPos;//鼠标按下时位置QPointF m_topLeftPos;//自定义窗口左上角的位置bool m_isPress;//鼠标左键是否按下//功能函数定义
void QCustomWidget::mousePressEvent(QMouseEvent *event)
{qDebug()<<"mousePressEvent";if(event->button() == Qt::LeftButton){m_pressPos = event->position();//相对于接收事件的窗口或者项,事件发生时的位置坐标m_topLeftPos = geometry().topLeft();m_isPress = true;}qDebug()<<"m_pressPos="<<m_pressPos<<", m_topLeftPos="<<m_topLeftPos;QWidget::mousePressEvent(event);
}void QCustomWidget::mouseReleaseEvent(QMouseEvent *event)
{m_isPress = false;QWidget::mouseReleaseEvent(event);
}void QCustomWidget::mouseMoveEvent(QMouseEvent *event)
{qDebug()<<"mouseMoveEvent";if(m_isPress && event->type() == QEvent::MouseMove){QPointF nowPos= event->position();qDebug()<<"nowPos="<<nowPos;move(QPoint((m_topLeftPos + nowPos - m_pressPos).x(),(m_topLeftPos + nowPos - m_pressPos).y()));qDebug()<<"x:"<<(m_topLeftPos + nowPos - m_pressPos).x()<<",y:"<<(m_topLeftPos + nowPos - m_pressPos).y();}QWidget::mouseMoveEvent(event);
}

以上只给主要相关代码,看懂就行。

运行结果

按照上面的代码运行之后,其效果就是本文开头所述那样,窗口拖动过程中出现抖动,且窗口不能实时跟随鼠标。

原因分析

上述代码中,鼠标按下函数mousePressEvent的实现代码里,获取鼠标按下的位置,使用m_pressPos = event->position();即position()来得到鼠标按下的位置。position()获取的位置相对于其所在的窗口或图形项,一般用于图形视图框获取图形项的位置。

在以前可能没有问题,但是qt6中便明确指出:
在这里插入图片描述
当移动窗口来响应鼠标事件,使用globalPosition()来代替position()。

顺便来看一下pos()的官方描述:
在这里插入图片描述
返回相对于接收事件的窗口,鼠标光标所在的位置。Qt6让使用position()代替pos()。
经实践所知,使用pos()与position()获得的鼠标位置,在窗口拖动中都出现抖动或者窗口不能及时跟随鼠标移动的现象。

改进代码

将之前使用position()获取坐标位置的地方改为globalPos(),且将之前QPointF类型的变量 m_pressPos与m_topLeftPos改为QPoint 类型。因为globalPos()返回值类型为QPoint 。

    //窗口拖动QPoint m_pressPos;QPoint m_topLeftPos;bool m_isPress;void QCustomWidget::mousePressEvent(QMouseEvent *event)
{qDebug()<<"mousePressEvent";if(event->button() == Qt::LeftButton){m_pressPos = event->globalPos();m_topLeftPos = geometry().topLeft();m_isPress = true;}qDebug()<<"m_pressPos="<<m_pressPos<<", m_topLeftPos="<<m_topLeftPos;QWidget::mousePressEvent(event);
}void QCustomWidget::mouseReleaseEvent(QMouseEvent *event)
{m_isPress = false;QWidget::mouseReleaseEvent(event);
}void QCustomWidget::mouseMoveEvent(QMouseEvent *event)
{qDebug()<<"mouseMoveEvent";if(m_isPress && event->type() == QEvent::MouseMove){QPoint nowPos= event->globalPos();qDebug()<<"nowPos="<<nowPos;move(QPoint((m_topLeftPos + nowPos - m_pressPos).x(),(m_topLeftPos + nowPos - m_pressPos).y()));qDebug()<<"x:"<<(m_topLeftPos + nowPos - m_pressPos).x()<<",y:"<<(m_topLeftPos + nowPos - m_pressPos).y();}QWidget::mouseMoveEvent(event);
}

再去运行程序,就能够正常拖动窗口了,不会出现抖动和窗口不能及时跟随鼠标移动的情况。

运行结果

拖动窗口

globalPos()

关于函数globalPos(),官方给出的描述如下:
在这里插入图片描述
但发现官方也在Qt6中要求近可能不使用globalPos(),已经在Qt6被抛弃,避免在新的代码中使用。使用globalPosition().toPoint()来代替。

globalPosition()

官方的描述如下:
在这里插入图片描述
于是即使上述的globalPos()可以解决原来的窗口拖动时的抖动和窗口不能及时跟随鼠标移动问题,但还是使用globalPosition()来实现一下看看吧。

再次修改代码

将globalPos()改为globalPosition().toPoint()来获取鼠标的位置。

void QCustomWidget::mousePressEvent(QMouseEvent *event)
{qDebug()<<"mousePressEvent";if(event->button() == Qt::LeftButton){m_pressPos = event->globalPosition().toPoint();m_topLeftPos = geometry().topLeft();m_isPress = true;}qDebug()<<"m_pressPos="<<m_pressPos<<", m_topLeftPos="<<m_topLeftPos;QWidget::mousePressEvent(event);
}void QCustomWidget::mouseReleaseEvent(QMouseEvent *event)
{m_isPress = false;QWidget::mouseReleaseEvent(event);
}void QCustomWidget::mouseMoveEvent(QMouseEvent *event)
{qDebug()<<"mouseMoveEvent";if(m_isPress && event->type() == QEvent::MouseMove){QPoint nowPos= event->globalPosition().toPoint();qDebug()<<"nowPos="<<nowPos;move(QPoint((m_topLeftPos + nowPos - m_pressPos).x(),(m_topLeftPos + nowPos - m_pressPos).y()));qDebug()<<"x:"<<(m_topLeftPos + nowPos - m_pressPos).x()<<",y:"<<(m_topLeftPos + nowPos - m_pressPos).y();}QWidget::mouseMoveEvent(event);
}

运行结果

其结果与使用globalPos()一样,都解决了窗口拖动时的抖动和不能及时跟随鼠标移动的问题。

区别

下面为pos(),globalPos(),globalPosition(),position()的区别:

pos():
这个函数返回控件在其父控件坐标系中的位置。

返回一个QPoint对象,表示控件左上角相对于其父控件的x和y坐标。
如果控件没有父控件(即它是一个顶级窗口),则pos()返回的是相对于屏幕的坐标。

globalPos():
这个函数返回控件在全局屏幕坐标系中的位置。
同样返回一个QPoint对象,但表示的是控件左上角相对于整个屏幕的x和y坐标。
这对于顶级窗口和子控件都适用,因为它总是返回屏幕上的绝对位置。

globalPosition():
这个函数与globalPos()的功能基本相同, 都返回控件在全局屏幕坐标系中的位置。

返回的也是QPoint对象,表示控件在屏幕上的绝对位置。
在某些版本的Qt中,globalPosition()可能是globalPos()的同义词或别名。Qt6之后推荐使用globalPosition()。

position():
这个函数通常用于QGraphicsItem,在Qt的图形视图框架中。

它返回该图形项在其父项或场景中的位置。
返回一个QPointF对象,表示图形项左上角的x和y坐标。

这篇关于qt自定义窗口在拖动过程中出现抖动且拖动后位置看上去不对的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/885706

相关文章

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

k8s中实现mysql主备过程详解

《k8s中实现mysql主备过程详解》文章讲解了在K8s中使用StatefulSet部署MySQL主备架构,包含NFS安装、storageClass配置、MySQL部署及同步检查步骤,确保主备数据一致... 目录一、k8s中实现mysql主备1.1 环境信息1.2 部署nfs-provisioner1.2.

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

linux部署NFS和autofs自动挂载实现过程

《linux部署NFS和autofs自动挂载实现过程》文章介绍了NFS(网络文件系统)和Autofs的原理与配置,NFS通过RPC实现跨系统文件共享,需配置/etc/exports和nfs.conf,... 目录(一)NFS1. 什么是NFS2.NFS守护进程3.RPC服务4. 原理5. 部署5.1安装NF

MySQL使用EXISTS检查记录是否存在的详细过程

《MySQL使用EXISTS检查记录是否存在的详细过程》EXISTS是SQL中用于检查子查询是否返回至少一条记录的运算符,它通常用于测试是否存在满足特定条件的记录,从而在主查询中进行相应操作,本文给大... 目录基本语法示例数据库和表结构1. 使用 EXISTS 在 SELECT 语句中2. 使用 EXIS

SpringBoot AspectJ切面配合自定义注解实现权限校验的示例详解

《SpringBootAspectJ切面配合自定义注解实现权限校验的示例详解》本文章介绍了如何通过创建自定义的权限校验注解,配合AspectJ切面拦截注解实现权限校验,本文结合实例代码给大家介绍的非... 目录1. 创建权限校验注解2. 创建ASPectJ切面拦截注解校验权限3. 用法示例A. 参考文章本文

oracle 11g导入\导出(expdp impdp)之导入过程

《oracle11g导入导出(expdpimpdp)之导入过程》导出需使用SEC.DMP格式,无分号;建立expdir目录(E:/exp)并确保存在;导入在cmd下执行,需sys用户权限;若需修... 目录准备文件导入(impdp)1、建立directory2、导入语句 3、更改密码总结上一个环节,我们讲了