自定义窗口拖动
- 引言
- 开发环境
- 关键性代码
- 运行结果
- 原因分析
- 改进代码
- 运行结果
- 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坐标。