Qt 无边框无非客户区窗口拖动、缩放改变大小、鼠标变形,windows同款,完美实现

本文介绍了如何在Qt环境下实现无边框窗口的拖动和改变大小,通过重载`nativeEvent`函数及使用Windows API,解决了窗口闪烁问题。文中详细讲解了实现过程,包括鼠标事件处理、过滤器的使用以及解决Qt5.7中的一个bug,最后实现了与Windows系统一致的拖动和缩放效果。
摘要由CSDN通过智能技术生成

简单点说,是在windows下实现窗口拖动和改变大小,一种方法是重载mousePressEvent系列全家福函数,然后在mouseMoveEvent中实现边移鼠标窗口就一边重绘。这种方法网上资料很多,见下:

https://www.cnblogs.com/findumars/p/5518590.html

这种方法缺点是移动的时候窗口闪烁厉害。摒弃。但该方法给了一个很好的思路。

另一种方法:

看到MFC中有调用NCHitTest方法的,这个方法是当鼠标触碰到非客户区时触发,然后调用windows api,实现窗口移动,参照下:

https://bbs.csdn.net/topics/350134227

在这篇帖子的最下方,有MFC改变窗口的方法。

于是我也想到了用Qt调windows api,反正我也不想做跨平台的软件,咳咳。然后发现了Qt有nativeEvent函数,捕获本地所有事件,重载之,专门处理鼠标移动信号。ok,实现了,但绝不完美!为何,继续看。

重载nativeEvent的方法网上查查,有第三个参数Long* result,result=HTLEFT时,鼠标就会变成横向双箭头图案,其他就参照MFC实现吧,后面会给出代码。

然后我们来看哪里不完美。Qt没有非客户区的概念,但类之间的信号传递很明显,当顶层窗口的最外层布局这样设置:

m_layoutMain->setMargin(0);

那么鼠标移到边缘时,鼠标信号的捕获是被最上层的子控件捕获了,实际上就触发不了顶层窗口的nativeEvent了。退而求其次,以为把边界设成5就可以了么?是的,不美观点,设成5确实就可以了,但如果想触发拖动事件的范围也是5,就会发现,鼠标在边界的5范围内,是横向双箭头图案,继续往里移,OK,进入子控件了,鼠标移动事件不触发了,鼠标一直是横向双箭头图案,何况,5也是接受不了的,我要0,不能忍,继续想办法。

然后就想到了过滤器,接着发现了Qt5.7的一个bug。过滤器,在顶层窗口实现nativeEventFilter方法,接着在qApp安装过滤器。

过滤器其实一点都不复杂,选个类A,在类A中实现nativeEventFilter方法后,建个对象a,只要在其他类用installNativeEventFilter(&a)方法安装过滤器,那个这个被安装过滤器的类,接收到了事件后,就会优先发给a来处理,而在qApp中安装过滤器,qApp是第一个接收到消息的,会全部先丢给a处理,a吃剩的残羹冷炙还给qApp,qApp再拿去分给其他改领盒饭的类。怎么算吃剩呢,在过滤器中return false就是吃剩不要了。然后上代码了

这是Class A中的方法,判断鼠标是否在规定

Qt实现边框窗口移动、拉伸、缩放需要使用QWidget的一些事件和函数。具体实现步骤如下: 1.设置无边框窗口 在QWidget中通过设置setWindowFlags函数来实现边框窗口。例如: ``` setWindowFlags(Qt::FramelessWindowHint); ``` 2.移动窗口 在QWidget中通过重写鼠标按下、移动、释放事件来实现窗口移动。例如: ``` void mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_isPressed = true; m_startPoint = event->globalPos() - this->geometry().topLeft(); } } void mouseMoveEvent(QMouseEvent *event) { if (m_isPressed) { this->move(event->globalPos() - m_startPoint); } } void mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_isPressed = false; } } ``` 其中m_isPressed记录鼠标左键是否按下,m_startPoint记录鼠标按下时的位置与窗口左上角的距离,this->move函数实现窗口移动。 3.拉伸窗口 在QWidget中通过重写鼠标按下、移动、释放事件来实现窗口拉伸。例如: ``` void mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_isPressed = true; m_startPoint = event->globalPos(); m_startGeometry = this->geometry(); } } void mouseMoveEvent(QMouseEvent *event) { if (m_isPressed) { QPoint diff = event->globalPos() - m_startPoint; if (m_resizeDir == RESIZE_DIR_LEFT_TOP) { this->setGeometry(m_startGeometry.x() + diff.x(), m_startGeometry.y() + diff.y(), m_startGeometry.width() - diff.x(), m_startGeometry.height() - diff.y()); } else if (m_resizeDir == RESIZE_DIR_RIGHT_TOP) { this->setGeometry(m_startGeometry.x(), m_startGeometry.y() + diff.y(), m_startGeometry.width() + diff.x(), m_startGeometry.height() - diff.y()); } else if (m_resizeDir == RESIZE_DIR_LEFT_BOTTOM) { this->setGeometry(m_startGeometry.x() + diff.x(), m_startGeometry.y(), m_startGeometry.width() - diff.x(), m_startGeometry.height() + diff.y()); } else if (m_resizeDir == RESIZE_DIR_RIGHT_BOTTOM) { this->setGeometry(m_startGeometry.x(), m_startGeometry.y(), m_startGeometry.width() + diff.x(), m_startGeometry.height() + diff.y()); } } } void mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_isPressed = false; } } ``` 其中m_isPressed记录鼠标左键是否按下,m_startPoint记录鼠标按下时的位置,m_startGeometry记录窗口拉伸前的位置和大小,m_resizeDir记录拉伸方向(左上、右上、左下、右下),this->setGeometry函数实现窗口拉伸。 4.缩放窗口 在QWidget中通过重写wheelEvent事件来实现窗口缩放。例如: ``` void wheelEvent(QWheelEvent *event) { QPoint pos = event->pos(); if (event->delta() > 0) { this->resize(this->width() * 1.2, this->height() * 1.2); } else { this->resize(this->width() * 0.8, this->height() * 0.8); } QPoint newPos = this->mapFromGlobal(this->mapToGlobal(pos)); QPoint diff = newPos - pos; this->move(this->x() - diff.x(), this->y() - diff.y()); } ``` 其中event->delta()为正表示向上滚动,为负表示向下滚动,this->resize函数实现窗口缩放,this->mapFromGlobal和this->mapToGlobal函数用于将鼠标位置从全局坐标系转换为窗口坐标系,diff记录窗口缩放前后鼠标位置的差值,this->move函数实现窗口位置的调整。 以上就是在Qt实现边框窗口移动、拉伸、缩放的方法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值