【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

相关推荐
C++ 老炮儿的技术栈1 小时前
现代 C++(C++11 及以后)的移动语义
linux·c语言·开发语言·c++·github
sycmancia1 小时前
QT——Qt Creator工程介绍
开发语言·qt
deviant-ART1 小时前
为什么 Java 编译器要求 catch 块显式 return 或 throw
java·开发语言
无心水1 小时前
Python时间处理通关指南:datetime/arrow/pandas实战
开发语言·人工智能·python·pandas·datetime·arrow·金融科技
2301_810160951 小时前
C++与Docker集成开发
开发语言·c++·算法
wjs20241 小时前
PHP MySQL 使用 Order By 排序
开发语言
CSDN_Colinw1 小时前
C++模块化设计原则
开发语言·c++·算法
草原上唱山歌1 小时前
如何理解C语言中的指针?
c语言·开发语言·数据结构
m0_662577972 小时前
C++中的模板方法模式
开发语言·c++·算法