Qt无边框窗体-最大化时支持拖拽还原
原文链接:Qt无边框窗体-最大化时支持拖拽还原
一、概述
用Qt进行开发界面时,既想要实现友好的用户交互又想界面漂亮,那么自定义界面就必不可少。其中有一个操作就是是我们每一个Qter开发者都要会的,而且是经常进行的。
Qt::FramelessWindowHint这个属性想必大家都使用过,有些同学可能对这个属性很了解,也用的是炉火纯青,今天我们也来说说这个属性。
关于这个无边框属性网上也有一些文章,有些谈论的是bug,当然了这是针对不同os而言,也有些是跟其他第三方库混合使用时的问题。可是问题归问题,想要实现自定义的优秀界面这个属性也是必不可少的。
今天我们就来实现一个无边框窗体最大化时,支持拖拽标题栏进行还原的功能。
无边框窗体支持缩放、移动这些不属于本篇文章的内容,本篇文章主要讲解怎么实现最大化时拖拽标题栏进行还原窗体,本篇文章的代码依赖于博主之前封装的一个拖拽代理类。
二、效果展示
如效果图所示,做了一个简单的事例,双击标题栏窗体最大化,这个时候如果进行标题栏拖拽,当鼠标按下并移动一段距离时窗体恢复normal状态。
恢复normal状态下的窗体仍然支持放大和缩小,有接口可以设置。

三、demo制作
demo的制作过程还是比较简单的,分为如下几步
1、设计窗体
通过desinger设计器我们拖拽了一个大致窗体内容,为了更好的展示效果,标题栏加上了icon和背景色

2、双击放大
鼠标双击标题栏放大这个功能实现起来方法也比较多,这里博主选择了代码量最少并且实现起来最简单的方式,直接把标题栏的事件循环安装到了主窗体上。
ui.widget->installEventFilter(this);
接下来我们就需要重写主窗口的eventFilter函数即可
bool DragWidget::eventFilter(QObject * watched, QEvent * event)
{
if (watched == ui.widget)
{
if (event->type() == QEvent::MouseButtonDblClick)
{
if (isMaximized())
{
showNormal();
m_handler.setWidgetResizable(true);
m_handler.setWidgetMovable(true);
}
else
{
showMaximized();
m_handler.setWidgetResizable(false);
m_handler.setWidgetMovable(false);
}
}
}
return QWidget::eventFilter(watched, event);
}
细心的同学就会发现代码里有一个m_handler变量,这个类就是博主之前自己封装的一个拖拽代理,通过接口可以设置被代理的窗体,并设置需要代理哪些行为。
本篇文章中所演示的事例代码,我们代理了主窗口上标题栏部分的移动事件和整个窗体的缩放事件,设置代码如下所示
m_handler.activateOn(this);
m_handler.useLocalMoveabled(true);
m_handler.addLocalWidget(ui.widget);
m_handler.setMaximumMove(true, true);
拖拽代理类内容比较多,本篇文章暂不讲解。
四、拖拽
为了更好的理解本篇文章,这里需要把拖拽代理类的头文件放出来,这样更有利于大家理解。
接口都比较简单,代码中也有注释,大家自行阅读。
class WidgetResizeHandler : public QObject
{
public:
explicit WidgetResizeHandler(QObject* parent = 0);
~WidgetResizeHandler();
public:
void activateOn(QWidget * topLevelWidget);//添加topLevelWidget事件代理
void removeFrom(QWidget * topLevelWidget);//移除topLevelWidget事件代理
Qt::CursorShape CursorShape(QWidget * widget);
//窗口移动 default:true
void setWidgetMovable(bool movable);
bool isWidgetMovable();
//大小可变 default:true
void setWidgetResizable(bool resizable);
bool isWidgetResizable();
// 橡胶式窗口移动 default:false
void useRubberBandOnMove(bool use);
bool isUsingRubberBandOnMove();
//橡胶式修改大小 default:false
void useRubberBandOnResize(bool use);
bool isUsingRubberBandOnResisze();
void setBorderWidth(int newBorderWidth);
int borderWidth();
//局部可移动
void useLocalMoveabled(bool use);
void addLocalWidget(QWidget *);
//最大化时支持拖拽 参数2表示是否可放大缩小
void setMaximumMove(bool move, bool resize = false);
protected:
virtual bool eventFilter(QObject * obj, QEvent * event) Q_DECL_OVERRIDE;
private:
WidgetResizeHandlerImpl * d_ptr;
};
值得注意的是最后一个setMaximumMove接口,他就是我们今天的猪脚-是否支持最大化时拖拽。当我们设置了这个接口后,窗体最大化时也就能进行拖拽,并还原到之前的normal状态。
文章第三小节讲解demo时,说过主窗体已经被代理拖拽类进行了事件代理,那么主窗体的所有事件首先都会传递给这个代理类,这里我们需要重点关注下鼠标按下时移动事件。
void WidgetData::handleMouseMoveEvent(QMouseEvent* event)
{
if (mLeftButtonPressed)
{
if (d_ptr->mWidgetResizable && mPressedMousePos.onEdges)
{
resizeWidget(event->globalPos());
}
else if (d_ptr->mWidgetMovable)
{
moveWidget(event->globalPos());
}
else if (d_ptr->mMaxMovable)
{
if (mWidget->isMaximized() && TryMoveWidget(event))
{
d_ptr->mWidgetMovable = true;
//d_ptr->mWidgetResizable = true;
}
}
}
else if (d_ptr->mWidgetResizable)
{
updateCursorShape(event->globalPos());
}
}
这段代码包含有其他缩放窗体和正常移动的逻辑,最大化时支持移动的逻辑应该不难找木九十TryMoveWidget这个函数,该函数中我们进行了充分的逻辑判断,一旦触发了窗体移动,那么我们把mWidgetMovable变量置为true,下一次鼠标按下移动事件就会触发正常的拖拽逻辑。
仔细思考上边一段话,其中有2个关键信息
- 触发窗体移动,并还原到之前的normal状态
- 进行了第一步后,需要把mWidgetMovable变量置为true,之后走正常的窗体移动流程
窗体移动
尝试移动窗体,当鼠标当前位置距离鼠标按下时的距离大于20px时,进行窗体还原操作,并返回true,代表窗体已经被重置到normal态。
bool WidgetData::TryMoveWidget(QMouseEvent* event)
{
QPoint distance = event->globalPos() - mDragPos;
int length = distance.manhattanLength();
if (length > 20)
{
QRect rect = mWidget->normalGeometry();
int desX = mDragPos.x() * rect.width() / mWidget->geometry().width();
int desY = mDragPos.y();
rect.moveTopLeft(event->globalPos() - QPoint(desX, desY));
mWidget->showNormal();
mWidget->setGeometry(rect);
mDragPos = QPoint(desX, desY);
mIsMaxMove = true;
return true;
}
return false;
}
上述代码中的mIsMaxMove标识是为了在一次窗体还原操作后,释放鼠标时可以正常的设置缩放标识而设。
有了上述代码之后,窗体就能还原到最大化之前的大小,并且为之也移动到了鼠标相应的位置,关于这个新位置的计算这里需要说明下。
x坐标
x轴坐标使用了比例计算方式。窗体全屏时鼠标按下的位置在窗体上的位置在窗体还原后依然保持不变,这样计算比较简单而且不会出错,保证窗体还原后,鼠标会一直在标题栏内。
如果需要优化x轴坐标的计算方法,只需要重新计算上述代码中的desX值即可。
y坐标
y轴坐标这里没有做特殊处理。因为窗体还原时,标题栏的高度是没有发生变化的,因此这里不需要做特殊处理。
讲到这里,本篇文章的主要内容基本完成,关于代理拖拽类,不属于本篇文章内容,因此就不做过多解释。
五、相关文章
值得一看的优秀文章:
![]() |
![]() |
很重要--转载声明
本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。
Qt无边框窗体-最大化时支持拖拽还原的更多相关文章
- Qt无边框窗体-模拟模态窗体抖动效果
目录 一.概述 二.效果展示 三.功能实现 四.相关文章 原文链接:Qt无边框窗体-模拟模态窗体抖动效果 一.概述 用Qt开发windows客户端界面确实是一大利器,兼顾性能的同时,速度相对来说也不错 ...
- Qt 无边框窗体改变大小 完美实现(全部自己实现)
近期,做项目用到无边框窗体,令人蛋疼的是无边框窗体大小的改变要像右边框那样,上下左右四周,而且要流畅. 网上也找了些代码,发现居然还要连接到windows事件,这显然不合常理,后来自己新建了demo, ...
- Qt 无边框窗体改变大小 完美实现
近期,做项目用到无边框窗体,令人蛋疼的是无边框窗体大小的改变要像右边框那样,上下左右四周,而且要流畅. 网上也找了些代码,发现居然还要连接到windows事件,这显然不合常理,后来自己新建了demo, ...
- 【C#】使用DWM实现无边框窗体阴影或全透窗体
1.无边框窗体阴影,win7(需要开启Aero效果)及以上系统 public class LdwmForm : Form { public LdwmForm() { Initialize(); } / ...
- Qt 无边框拖拽实现
Qt 无边框拖拽实现 头文件定义: class TDragProxy:public QObject { Q_OBJECT public: TDragProxy(QWidget* parent); ~T ...
- Winform拖拽改变无边框窗体大小
大家在进行Winform开发过程中,很容易就可以完成一个窗口的布局工作,但现在的软件界面美化效果一个比一个好,很多软件都是无边框的,于是乎,你是不是也感叹:winform的带边框的窗体如此丑陋,我一定 ...
- Qt:移动无边框窗体(使用Windows的SendMessage)
移动无边框窗体的代码网上很多,其原理都是一样的,但是是有问题的,我这里只是对其修正一下 网上的代码仅仅实现了两个事件 void EditDialog::mousePressEvent(QMouseEv ...
- qt动态库实现无边框窗体的消息处理 nativeEvent的使用
需求: 在动态库中创建一个窗口句柄,可以给外部调用,库的调用者,通过这个句柄发送消息到底层库,库里面可以实现对消息的处理 m_FHandle=AllocateHWnd(WndProcDllMsg); ...
- MFC 无边框窗体实现用鼠标拖动窗体边缘实现窗体大小变化
无边框窗体如何实现用鼠标拖动窗体边缘实现窗体大小变动呢?下面介绍一种方法,通过以下几个步骤即可实现: 1.实现WM_NCHITTEST消息,实现四条边框的模拟 2.实现WM_NCLBUTTONDOWN ...
随机推荐
- MyBatis框架之SQL映射和动态SQL
使用MyBatis实现条件查询 1.SQL映射文件: MyBatis真正的强大之处就在于SQL映射语句,MyBatis专注于SQL,对于开发人员来说也是极大限度的进行SQL调优,以保证性能.下面是SQ ...
- React躬行记(10)——高阶组件
高阶组件(High Order Component,简称HOC)不是一个真的组件,而是一个没有副作用的纯函数,以组件作为参数,返回一个功能增强的新组件,在很多第三方库(例如Redux.Relay等)中 ...
- python以ATM+购物车剖析一个项目的由来及流程
ATM+购物车 一个项目是如何从无到有的 ''' 项目的由来,几个阶段 0.采集项目需求 1.需求分析 2.程序的架构设计 3.分任务开发 4.测试 5.上线运行 ''' 需求分析: # 对项目需求进 ...
- win8上安装sql server (2008 and 2008 R2)存在兼容性问题怎么解决
安装sql2008R2之前,需要先下载"AccessDatabaseEngine"组件并安装(百度上搜索下载就可以了),打补丁重启,再安装sql2008,安装时候提示兼容性问 ...
- 使用.net core基于Razor Pages开发网站一些工作笔记
本文是在实践工作中遇到的一些问题记录,并给出是如何解决的,.net core已经升级到3.0版本了,其实在项目中很早就已经在使用.net core来开发后台接口了,正好有个网站项目,就使用了Razor ...
- SpringBoot日志相关
SpringBoot使用的是SLF4j当门面,Logback当实现完成 日志级别 数字越大,级别越高,框架只会输出大于等于当前日志级别的信息 ERROR 40 WARN 30 INFO 20 DEBU ...
- java使用栈计算后缀表达式
package com.nps.base.xue.DataStructure.stack.utils; import java.util.Scanner; import java.util.Stack ...
- 这 3 个 Set 集合的实现有点简单,那来做个总结吧
Set 接口是 Java Collections Framework 中的一员,它的特点是:不能包含重复的元素,允许且最多只有一个 null 元素.Java 中有三个常用的 Set 实现类: Hash ...
- 【Algorithm】插入排序法
通常人们整理桥牌的方法是一张一张的来,将每一张插入到其他已经有序的牌中的适当位置. • 思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的序列的合适位置,直到全部插入排序完为止. Jav ...
- 简易数据分析 08 | Web Scraper 翻页——点击「更多按钮」翻页
这是简易数据分析系列的第 8 篇文章. 我们在Web Scraper 翻页--控制链接批量抓取数据一文中,介绍了控制网页链接批量抓取数据的办法. 但是你在预览一些网站时,会发现随着网页的下拉,你需要点 ...

