【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

相关推荐
catchadmin几秒前
免费可商用 PHP 管理后台 CatchAdmin V5.3.1 发布 后台打包直降 5s 内
开发语言·php
YY&DS10 分钟前
Qt Designer 自定义控件已提升后,如何修改提升类
开发语言·qt
右耳朵猫AI19 分钟前
Rust技术周刊 2026年第19周
开发语言·后端·rust
Leweslyh30 分钟前
基于 Confucius 架构的无人集群网络控制原语解析
开发语言·网络·php
月落归舟42 分钟前
Java线程小记
java·开发语言
摇滚侠1 小时前
01 基础语法 JavaScript 入门到精通全套教程
开发语言·javascript·ecmascript
sleven fung1 小时前
Milvus 向量数据库
开发语言·数据库·python·langchain·milvus
大大杰哥1 小时前
Java 日志框架详解:SLF4J + Logback 从入门到实战
java·开发语言·logback
ylscode1 小时前
黑客利用 GHOSTYNETWORKS 和 OMEGATECH 托管 JS 恶意软件基础设施
开发语言·安全·php·安全威胁分析
爱吃生蚝的于勒1 小时前
QT开发第二章——信号和槽
c语言·开发语言·c++·qt