【Qt】 无边框窗口方案

Qt 无边框窗口方案

一、方案整体概述

目标:去掉系统原生标题栏和边框,自定义界面样式,同时保留窗口拖拽、边缘缩放、最大/最小/关闭等完整功能

整体架构

复制代码
1. 界面层:Frame_Title(自定义标题栏,UI + 按钮交互)
2. 调度层:FramelessHelper(事件过滤器,统一接管鼠标事件)
3. 数据层:WidgetData(单窗口状态管理,执行拖拽/缩放/光标切换)
4. 计算层:CursorPosCalculator(鼠标位置判断,核心算法)

二、核心设计原理

1. 窗口基础:无边框标志设置

作用

移除系统自带的标题栏、边框、窗口装饰,为自定义界面铺路。

关键代码
cpp 复制代码
setWindowFlags(Qt::Window 
               | Qt::FramelessWindowHint  // 核心:无边框
               | Qt::WindowSystemMenuHint
               | Qt::WindowMinimizeButtonHint);
标志说明
  • Qt::FramelessWindowHint真正实现无边框
  • Qt::Window:保证控件是顶层窗口
  • 后两个:保留系统菜单与最小化能力

2. 计算核心:CursorPosCalculator(鼠标位置判断)

整个方案的"眼睛",负责判断鼠标在哪里。

核心功能
  • 判断鼠标是否在窗口边缘(用于缩放)
  • 判断鼠标是否在标题栏区域(用于拖拽)
  • 判断鼠标是否在四角/四边(切换对应光标)
关键静态参数(全局生效)
cpp 复制代码
static int m_nBorderWidth;  // 边缘缩放宽度,默认 5px
static int m_nTitleHeight;  // 标题栏拖拽高度,默认 30px
核心算法

通过全局鼠标坐标窗口全局矩形做差值计算:

cpp 复制代码
m_bOnLeftEdge = (mouseX >= rect.x() && mouseX <= rect.x() + 边框宽度);
m_bOnTitle = (mouseY >= rect.y() && mouseY <= rect.y() + 标题栏高度);

3. 事件处理核心:WidgetData

每个窗口对应一个 WidgetData 实例,负责:

  • 鼠标按下/移动/释放/离开的事件处理
  • 窗口拖拽逻辑
  • 窗口边缘缩放逻辑
  • 光标样式自动切换
  • 橡皮筋效果(可选)
核心方法
  1. updateCursorShape():根据鼠标位置自动切换光标(箭头→水平缩放→斜向缩放)
  2. moveWidget():窗口拖拽
  3. resizeWidget():窗口边缘缩放
  4. 四大鼠标事件处理:按下/移动/释放/离开
拖拽原理
  • 鼠标按下标题栏 → 记录起始偏移
  • 鼠标移动 → 窗口位置 = 鼠标位置 - 起始偏移
  • 鼠标释放 → 结束拖拽
缩放原理
  • 判断鼠标落在哪个边缘/角落
  • 动态修改窗口矩形 setGeometry()
  • 限制最小宽高,防止缩到 0

4. 调度核心:FramelessHelper

**使用 Qt 事件过滤器 技术。

核心能力
  • 给任意顶层窗口安装事件过滤器
  • 拦截鼠标相关事件(按下/移动/释放/离开/悬停)
  • 将事件分发给对应 WidgetData 处理
  • 支持多窗口同时管理
关键机制
cpp 复制代码
bool eventFilter(QObject *obj, QEvent *event) {
    if(是鼠标事件) {
        找到对应窗口的 WidgetData
        data->handleWidgetEvent(event); // 交给数据层处理
        return true; // 拦截事件,不传给原窗口
    }
    return false;
}

5. UI 核心:Frame_Title 自定义标题栏

替代系统标题栏。

功能清单
  • 标题文字居中显示
  • 最小化 / 最大化 / 关闭按钮
  • 自定义图标、背景图片
  • 跟随主窗口宽度自动适配
  • 点击拖拽主窗口
关键实现
  1. 布局结构
    • 标题标签居中
    • 按钮组右对齐悬浮
    • 互不干扰
  2. 大小同步
    • 监听主窗口 resize 事件
    • 实时更新标题栏宽度
  3. 样式完全自定义
    • 支持背景图 border-image
    • 支持按钮图标
    • 支持 QSS 样式表

三、完整使用方法(一步一照抄)

步骤 1:导入文件

将以下文件加入项目:

复制代码
CursorPosCalculator.cpp/h
WidgetData.cpp/h
FramelessHelper.cpp/h
FramelessHelperPrivate.h
Frame_Title.cpp/h

.pro 文件自动包含即可。

步骤 2:主窗口构造函数初始化

cpp 复制代码
// 1. 设置无边框标志
setWindowFlags(Qt::Window | Qt::FramelessWindowHint | ...);

// 2. 创建无边框助手
framelessHelper = new FramelessHelper(this);
framelessHelper->activateOn(this);       // 激活当前窗口
framelessHelper->setWidgetMovable(true); // 可拖拽
framelessHelper->setWidgetResizable(true);//可缩放

// 3. 创建标题栏
titleBar = new Frame_Title(ui->centralwidget);
titleBar->setActive(this); // 绑定主窗口

步骤 3:布局重构

  1. 删除中央控件原有布局
  2. 创建垂直布局
  3. 顶部加入标题栏
  4. 下面放入原来的业务布局
cpp 复制代码
QVBoxLayout *newLayout = new QVBoxLayout(centralWidget);
newLayout->setContentsMargins(0,0,0,0);
newLayout->addWidget(titleBar);    // 标题栏在最顶上
newLayout->addLayout(你的业务布局);

步骤 4:配置标题栏样式

cpp 复制代码
titleBar->setTitle("无边框窗口");
titleBar->setMinIcon("路径/min.png");
titleBar->setMaxIcon("路径/max.png");
titleBar->setCloseIcon("路径/close.png");
titleBar->setBackgroundImage("路径/background.png");

四、关键 API 速查表

函数 作用
FramelessHelper activateOn(widget) 给窗口启用无边框
setWidgetMovable(true) 允许拖拽
setWidgetResizable(true) 允许缩放
setTitleHeight(30) 设置标题栏拖拽高度
Frame_Title setActive(widget) 绑定主窗口
setTitle() 设置标题
setMin/Max/CloseIcon 设置按钮图标
setBackgroundImage 设置背景图

无边框效果

https://gitee.com/sun874573943/no-frame-border.git

相关推荐
爱吃烤鸡翅的酸菜鱼3 分钟前
【Java】封装位运算通用工具类——用一个整数字段替代几十个布尔列,极致节省存储空间
java·开发语言·设计模式·工具类·位运算·合成复用原则
xinhuanjieyi6 分钟前
php给30支NBA球队添加logo图标,做好对应关系
android·开发语言·php
菜菜小狗的学习笔记9 分钟前
八股(三)Java并发
java·开发语言
史迪仔011212 分钟前
[QML] 交互事件深度解析:鼠标、键盘、拖拽
前端·c++·qt
一晌小贪欢27 分钟前
PyQt5 开发一个 PDF 批量合并工具
开发语言·qt·pdf
神仙别闹27 分钟前
基于 MATLAB 实现的图像信号处理
开发语言·matlab·信号处理
swift1922133 分钟前
Qt多语言问题 —— 静态成员变量
开发语言·c++·qt
それども37 分钟前
Spring Bean @Autowired自注入空指针问题
java·开发语言·spring
用户8055336980339 分钟前
现代Qt开发教程(新手篇)1.4——容器
c++·qt
沐知全栈开发44 分钟前
JavaScript for 循环
开发语言