Qt C++ 教程:无边框窗体 + 自定义标题栏 + 圆角 + 拖拽拉升 + 阴影
在使用Qt做界面开发时,为了提升视觉效果,经常会采用无边框窗口设计。
实现无边框其实很简单,一行代码搞定。
setWindowFlag(Qt::FramelessWindowHint);
由于移除了系统默认标题栏,窗口失去了原生的移动和缩放功能,需通过代码手动实现。
本文旨在使用 Qt 框架实现一个无边框窗口,具备以下核心功能:
- 去除系统默认的窗口边框和标题栏;
- 支持通过鼠标拖动实现窗口整体移动;
- 预留最小化、最大化、关闭等标准窗口操作的接口结构(按钮功能将在后续章节中实现)。
该方案适用于希望自定义窗口外观、提升界面美观度的 Qt 开发者,尤其适合初学者理解无边框窗口的基本实现原理。
核心技术点
技术 | 说明 |
setWindowFlags(Qt::FramelessWindowHint) | 移除系统默认边框和标题栏 |
setWindowFlags(Qt::WindowSystemMenuHint) | 保留系统右键菜单(如移动、大小调整等),提升用户体验 |
重写 mousePressEvent | 记录鼠标按下时的相对位置,为拖动做准备 |
重写 mouseMoveEvent | 根据鼠标移动实时更新窗口位置,实现拖动效果 |
Part1无边框窗口实现
1.1、头文件定义
BasicFramelessWindow.h
#pragma once
#include <QtWidgets/QWidget>
#include <QMouseEvent>
class BasicFramelessWindow : public QWidget
{Q_OBJECT
public:explicit BasicFramelessWindow(QWidget *parent = nullptr);~BasicFramelessWindow();
protected:void mousePressEvent(QMouseEvent* event) override;void mouseMoveEvent(QMouseEvent* event) override;
private:QPoint dragPosition; // 记录鼠标按下时相对于窗口左上角的偏移
};
说明:
- 继承自 QWidget,构建基础窗口;
- 定义两个受保护的事件处理函数,用于捕获鼠标行为;
- 使用 dragPosition 存储拖动起始点与窗口坐标之间的偏移量。
1.2、源文件实现
BasicFramelessWindow.cpp
#include "BasicFramelessWindow.h"
BasicFramelessWindow::BasicFramelessWindow(QWidget *parent): QWidget(parent)
{// 设置窗口为无边框,并保留系统菜单(右键可调出系统操作)setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);// 关闭透明背景(确保背景正常显示)setAttribute(Qt::WA_TranslucentBackground, false);// 设置固定窗口大小setFixedSize(600, 400);// 设置样式:白色背景 + 灰色边框(便于观察)setStyleSheet("background-color: white; border: 1px solid gray;");
}
BasicFramelessWindow::~BasicFramelessWindow()
{// 析构函数(当前无需特殊处理)
}
// 鼠标按下事件:记录拖动起始位置
void BasicFramelessWindow::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {// 计算鼠标点击位置与窗口左上角的偏移dragPosition = event->globalPosition().toPoint() - frameGeometry().topLeft();event->accept(); // 接受事件,防止被其他控件处理}
}
// 鼠标移动事件:执行窗口拖动
void BasicFramelessWindow::mouseMoveEvent(QMouseEvent *event)
{if (event->buttons() & Qt::LeftButton) {// 根据偏移量移动窗口move(event->globalPosition().toPoint() - dragPosition);event->accept();}
}
关键逻辑解析:
- event->globalPosition().toPoint():获取鼠标在屏幕坐标系中的位置;
- frameGeometry().topLeft():获取窗口在屏幕上的左上角坐标;
- 两者的差值即为“拖动锚点”,确保鼠标始终“抓着”窗口同一位置移动;
- move(...)直接改变窗口位置,实现平滑拖动。
运行程序后,将显示一个 600×400 的白色无边框窗口
虽然界面空白,但已具备以下能力:
- 可通过鼠标左键点击并拖动窗口任意位置实现移动
- 窗口无系统标题栏和边框
- 保留系统右键菜单(可通过右键点击任务栏图标调出“移动”“大小”等选项)
Part2实现自定义标题栏
实现自定义标题栏:支持拖动、双击最大化与动态按钮切换
2.1、功能目标
功能 | 说明 |
自定义标题栏 | 替代系统默认标题栏,支持自由布局与样式定制 |
三按钮控制 | 最小化、最大化/还原、关闭,通过信号与主窗口通信 |
动态图标切换 | 窗口最大化时,“最大化按钮”自动变为“还原图标” |
拖动移动 | 鼠标按住标题栏可拖动窗口(非最大化状态下) |
双击切换状态 | 双击标题栏实现最大化 ↔ 正常状态切换 |
2.2、标题栏组件实现(TitleBar)
我们将标题栏封装为独立组件 TitleBar,继承自 QWidget,便于在不同窗口中复用。
1. 头文件:TitleBar.h
#pragma once
#include <QWidget>
#include <QPushButton>
#include <QLabel>
#include <QHBoxLayout>
#include <QMouseEvent>
class TitleBar : public QWidget
{Q_OBJECT
public:explicit TitleBar(QWidget* parent = nullptr);// 设置当前是否为最大化状态,用于图标切换void setMaximized(bool maximized);
signals:void signalMinimize(); // 发送最小化信号void signalMaximizeRestore(); // 发送最大化/还原切换信号void signalClose(); // 发送关闭信号
protected:void mousePressEvent(QMouseEvent* event) override;void mouseMoveEvent(QMouseEvent* event) override;void mouseDoubleClickEvent(QMouseEvent* event) override;
private:QPushButton* btnMin; // 最小化按钮QPushButton* btnMaxRestore; // 最大化/还原按钮QPushButton* btnClose; // 关闭按钮QLabel* titleLabel; // 标题标签QPoint dragPosition; // 拖动偏移量bool isMaximized; // 当前是否最大化
};
设计说明:
- 这里头定义了个 TitleBar 类,继承自 QWidget。
- public 里有构造函数和设置图标状态的 setMaximized 方法;
- signals 那块是三个信号,对应最小化、最大化 / 还原、关闭这几个动作;
- protected 里重载了鼠标按下、移动和双击事件;
- private 里就是那三个按钮、标题标签、记录拖动位置的 dragPosition,还有标记是否最大化的 isMaximized。
2. 源文件:TitleBar.cpp
#include "TitleBar.h"
#include <QMouseEvent>
#include <QStyle>
#include <QApplication>
TitleBar::TitleBar(QWidget* parent): QWidget(parent)
{isMaximized = false;setFixedHeight(35); // 标题栏高度setAttribute(Qt::WA_StyledBackground, true); // 启用样式表渲染// 设置整体样式setStyleSheet(R"(TitleBar {background-color: rgb(223, 235, 250);}QPushButton {border: none;background-color: transparent;min-width: 45px;min-height: 35px;}QPushButton:hover {background-color: rgb(211, 226, 237);}QPushButton:pressed {background-color: rgba(255, 255, 255, 50);})");// 标题文本titleLabel = new QLabel("My App");titleLabel->setStyleSheet("border:none; background:transparent; font-weight:bold; padding-left:10px;");// 创建按钮并设置图标(需确保资源已添加到qrc)btnMin = new QPushButton();btnMin->setIcon(QIcon(":/new/prefix1/resources/min.png"));btnMaxRestore = new QPushButton();btnMaxRestore->setIcon(QIcon(":/new/prefix1/resources/max.png"));btnClose = new QPushButton();btnClose->setIcon(QIcon(":/new/prefix1/resources/close.png"));// 布局管理auto mainLayout = new QHBoxLayout(this);mainLayout->addWidget(titleLabel);mainLayout->addStretch();mainLayout->addWidget(btnMin);mainLayout->addWidget(btnMaxRestore);mainLayout->addWidget(btnClose);mainLayout->setContentsMargins(0, 0, 0, 0);mainLayout->setSpacing(0);// 信号连接connect(btnMin, &QPushButton::clicked, this, &TitleBar::signalMinimize);connect(btnMaxRestore, &QPushButton::clicked, this, &TitleBar::signalMaximizeRestore);connect(btnClose, &QPushButton::clicked, this, &TitleBar::signalClose);
}
图标状态切换
void TitleBar::setMaximized(bool maximized)
{isMaximized = maximized;btnMaxRestore->setIcon(isMaximized ?QIcon(":/new/prefix1/resources/restore.png") : // 还原图标QIcon(":/new/prefix1/resources/max.png") // 最大化图标);
}
鼠标事件处理
void TitleBar::mousePressEvent(QMouseEvent* event)
{if (event->button() == Qt::LeftButton) {dragPosition = event->globalPosition().toPoint() - parentWidget()->frameGeometry().topLeft();}
}
void TitleBar::mouseMoveEvent(QMouseEvent* event)
{if ((event->buttons() & Qt::LeftButton) && !isMaximized) {parentWidget()->move(event->globalPosition().toPoint() - dragPosition);}
}
⚠️ 注意:仅在非最大化状态下允许拖动,避免误操作。
void TitleBar::mouseDoubleClickEvent(QMouseEvent* event)
{Q_UNUSED(event);emit signalMaximizeRestore(); // 双击触发最大化/还原
}
双击标题栏即可切换窗口状态,符合用户习惯。
2.3、整合至主窗口(BasicFramelessWindow)
接下来将 TitleBar 嵌入主窗口,并连接信号槽实现完整控制逻辑。
1. 更新头文件:BasicFramelessWindow.h
#pragma once
#include <QWidget>
#include <QMouseEvent>
#include "TitleBar.h"
class BasicFramelessWindow : public QWidget
{Q_OBJECT
public:explicit BasicFramelessWindow(QWidget* parent = nullptr);~BasicFramelessWindow();
protected:void mousePressEvent(QMouseEvent* event) override;void mouseMoveEvent(QMouseEvent* event) override;
private slots:void onMinimize();void onMaxRestore();void onClose();
private:TitleBar* titleBar;bool isMaximized;QPoint dragPosition;
};
2. 实现主窗口逻辑:BasicFramelessWindow.cpp
#include "BasicFramelessWindow.h"
#include <QVBoxLayout>
BasicFramelessWindow::BasicFramelessWindow(QWidget* parent): QWidget(parent), isMaximized(false)
{setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);setAttribute(Qt::WA_TranslucentBackground, false);setFixedSize(600, 400);setStyleSheet("background-color: white; border: 1px solid gray;");// 创建标题栏titleBar = new TitleBar(this);// 连接信号与槽connect(titleBar, &TitleBar::signalMinimize, this, &BasicFramelessWindow::onMinimize);connect(titleBar, &TitleBar::signalMaximizeRestore, this, &BasicFramelessWindow::onMaxRestore);connect(titleBar, &TitleBar::signalClose, this, &BasicFramelessWindow::onClose);// 主布局auto mainLayout = new QVBoxLayout(this);mainLayout->addWidget(titleBar);mainLayout->addStretch();mainLayout->setSpacing(0);mainLayout->setContentsMargins(1, 1, 1, 1); // 留出边框间隙
}
BasicFramelessWindow::~BasicFramelessWindow() = default;
void BasicFramelessWindow::mousePressEvent(QMouseEvent* event)
{if (event->button() == Qt::LeftButton) {dragPosition = event->globalPosition().toPoint() - frameGeometry().topLeft();event->accept();}
}
void BasicFramelessWindow::mouseMoveEvent(QMouseEvent* event)
{if (event->buttons() & Qt::LeftButton) {move(event->globalPosition().toPoint() - dragPosition);event->accept();}
}
// 槽函数实现
void BasicFramelessWindow::onMinimize()
{showMinimized();
}
void BasicFramelessWindow::onMaxRestore()
{if (isMaximized) {showNormal();} else {showMaximized();}isMaximized = !isMaximized;titleBar->setMaximized(isMaximized); // 同步按钮图标
}
void BasicFramelessWindow::onClose()
{close();
}
信号-槽机制实现松耦合;
窗口状态变化后同步更新标题栏图标;
支持最小化、最大化/还原、关闭全功能。
2.4、运行效果
Part3实现无边框窗口
接下来我们将实现一个关键功能: 像系统窗口一样,通过鼠标拖动窗口边缘或角落来调整大小。
这包括:
- 上、下、左、右四边拉伸
- 四个角(左上、右上、左下、右下)斜向缩放
- 鼠标悬停时显示对应方向的光标(↔、↕、↘ 等)
- 动态调整窗口尺寸,支持最小宽高限制
3.1、功能说明
功能 | 说明 |
捕捉鼠标位置 | 判断鼠标是否位于窗口边缘或角落区域 |
改变鼠标形状 | 显示为 ↔(水平)、↕(垂直)、↘(对角线)等系统缩放光标 |
响应鼠标拖动 | 按下后拖动鼠标,动态调整窗口大小 |
限制最小尺寸 | 防止窗口被缩到不可见或过小 |
3.2、代码实现
核心类设计:CustomWindow
我们将窗口缩放功能封装在 CustomWindow 类中,继承自 QWidget,作为所有需要无边框缩放功能窗口的基类。
CustomWindow.h
#pragma once
#include <QtWidgets/QWidget>
#include<QMouseEvent>
#include<qpoint.h>class CustomWindow :public QWidget
{Q_OBJECT
public:explicit CustomWindow(QWidget* parent = nullptr);
protected:void mousePressEvent(QMouseEvent* event) override;void mouseReleaseEvent(QMouseEvent* event)override;void mouseMoveEvent(QMouseEvent* event)override;void leaveEvent(QEvent* event)override;
private:enum ResizeRegion {NoEdge = 0,Left,Right,Top,Bottom,TopLeft,TopRight,BottomLeft,BottomRight};const int EDGE_MARGIN = 8; //边缘检测范围ResizeRegion getResizeRegion(const QPoint& pos);bool isResizing = false; //是否正在缩放ResizeRegion currentRegion = NoEdge;QPoint dragStartGlobalPos; //鼠标拖动起点QRect originalGeometry; //拖动时窗口原始位置
};
代码详解:
- 这里定义了个 CustomWindow 类,继承自 QWidget。
- protected 里重载了鼠标按下、释放、移动和离开事件。
- private 里搞了个枚举 ResizeRegion,把窗口的边缘和角落都分了类,从 NoEdge(不在边缘)到各个方向的边缘和角落。
- EDGE_MARGIN,设成 8,就是边缘检测的范围,鼠标离边缘这么近就算是在边缘区域了。
- getResizeRegion 方法是用来判断鼠标位置属于哪个区域的。
- 变量isResizing 标记是不是正在缩放,currentRegion 记当前鼠标在哪个区域,dragStartGlobalPos 是鼠标开始拖动时的全局位置,originalGeometry 是拖动前窗口的位置和大小。
CustomWindow.cpp
#include "CustomWindow.h"
CustomWindow::CustomWindow(QWidget* parent):QWidget(parent)
{setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint); //无边框setMouseTracking(true); //鼠标移动触发mouseMoveEvent
}
构造函数里,先设了无边框,然后 setMouseTracking (true),这样鼠标在窗口上动的时候,不用按鼠标键也能触发 mouseMoveEvent,方便检测鼠标位置换光标。
//准备拖动
void CustomWindow::mousePressEvent(QMouseEvent* event)
{if (event->button() == Qt::LeftButton && currentRegion != NoEdge) {isResizing = true;dragStartGlobalPos = event->globalPosition().toPoint();originalGeometry = geometry();}QWidget::mousePressEvent(event);
}
鼠标按下事件里,要是按的是左键,而且当前鼠标在边缘区域(不是 NoEdge),就把 isResizing 设为 true,记下拖动开始时鼠标的全局位置 dragStartGlobalPos,还有当时窗口的位置大小 originalGeometry。
//结束拖动
void CustomWindow::mouseReleaseEvent(QMouseEvent* event)
{isResizing = false;QWidget::mouseReleaseEvent(event);
}
鼠标释放的时候,就把 isResizing 改成 false,结束缩放。
//设置光标或拖动缩放
void CustomWindow::mouseMoveEvent(QMouseEvent* event)
{if (isResizing) {QPoint delta = event->globalPosition().toPoint() - dragStartGlobalPos;QRect newGeom = originalGeometry;switch (currentRegion) {case Left:newGeom.setLeft(originalGeometry.left() + delta.x());break;case Right:newGeom.setRight(originalGeometry.right() + delta.x());break;case Top:newGeom.setTop(originalGeometry.top() + delta.y());break;case Bottom:newGeom.setBottom(originalGeometry.bottom() + delta.y());break;case TopLeft:newGeom.setTopLeft(originalGeometry.topLeft() + delta);break;case TopRight:newGeom.setTopRight(originalGeometry.topRight() + delta);break;case BottomLeft:newGeom.setBottomLeft(originalGeometry.bottomLeft() + delta);break;case BottomRight:newGeom.setBottomRight(originalGeometry.bottomRight() + delta);break;default:break;}if (newGeom.width() >= minimumWidth() && newGeom.height() >= minimumHeight()) {setGeometry(newGeom);}}else {//设置鼠标光标形状ResizeRegion region = getResizeRegion(event->pos());currentRegion = region;switch (region) {case Left:case Right:setCursor(Qt::SizeHorCursor);break;case Top:case Bottom:setCursor(Qt::SizeVerCursor);break;case TopLeft:case BottomRight:setCursor(Qt::SizeFDiagCursor);break;case TopRight:case BottomLeft:setCursor(Qt::SizeBDiagCursor);break;default:unsetCursor();break;}}QWidget::mouseMoveEvent(event);
}
鼠标移动事件分两种情况:要是正在缩放(isResizing 为 true),就先算一下鼠标移动的距离 delta—— 当前鼠标全局位置减去开始拖动时的位置。然后根据 currentRegion,也就是当前缩放的区域,调整 newGeom(新的窗口位置大小)。比如是 Left 区域,就调整窗口的左边界;是 Right 就调右边界,四个角也各有对应的调整方式。调整完了,得检查新的宽度和高度是不是不小于最小尺寸,符合条件就用 setGeometry 设置新的窗口形状。
要是没在缩放,就调用 getResizeRegion 判断鼠标当前在哪个区域,然后根据区域换光标形状。比如左右边缘就用水平缩放的光标↔,上下边缘用垂直缩放的光标↕,对角就用对应的对角线光标,不在边缘就恢复默认光标。
//鼠标离开窗口,取消高亮
void CustomWindow::leaveEvent(QEvent* event)
{if (!isResizing)unsetCursor();QWidget::leaveEvent(event);
}
鼠标离开窗口时,要是没在缩放,就把光标恢复默认。
//获取边缘区域
CustomWindow::ResizeRegion CustomWindow::getResizeRegion(const QPoint& pos)
{bool onLeft = pos.x() <= EDGE_MARGIN;bool onRight = pos.x() >= width() - EDGE_MARGIN;bool onTop = pos.y() <= EDGE_MARGIN;bool onButtom = pos.y() >= height() - EDGE_MARGIN;if (onTop && onLeft)return TopLeft;if (onTop && onRight)return TopRight;if (onButtom && onLeft)return BottomLeft;if (onButtom && onRight)return BottomRight;if (onTop)return Top;if (onButtom)return Bottom;if (onLeft)return Left;if (onRight)return Right;return NoEdge;
}
getResizeRegion 方法就是判断鼠标位置 pos 属于哪个区域。先看是不是在左、右、上、下边缘(根据 EDGE_MARGIN 判断),然后组合一下,比如又在顶部又在左边,就是 TopLeft,依次类推,最后返回对应的区域。
3.3、整合代码逻辑
因为 CustomWindow 是单独的类,要在之前的 BasicFramelessWindow 里用上它的缩放功能,得把逻辑整合一下。
先改 BasicFramelessWindow.h,让它继承 CustomWindow,把重复的鼠标事件处理逻辑去掉:
#pragma once
#include"TitleBar.h"
#include"CustomWindow.h"
class BasicFramelessWindow : public CustomWindow
{Q_OBJECT
public:explicit BasicFramelessWindow(QWidget *parent = nullptr);~BasicFramelessWindow();
private slots:void onMinimize();void onMaxRestore();void onClose();
private:TitleBar* titleBar;bool isMaximized;
};
这样就不用自己处理鼠标事件了,直接用 CustomWindow 的。
再改 BasicFramelessWindow.cpp,把原来的鼠标事件处理代码删掉,调整构造函数:
BasicFramelessWindow::BasicFramelessWindow(QWidget *parent): CustomWindow(parent),isMaximized(false)
还要把原来的 setFixedSize () 改成 resize (),因为现在要能缩放窗口,不能固定大小了。
这么一整合,窗口的鼠标拉伸功能就全实现啦,拽着边缘或者角落就能随便调整大小了。
Part4圆角与阴影效果
咱接下来搞 “无边框窗口的圆角与阴影效果”。要是在现有程序里改,得动的地方不少,所以咱直接建个新项目来实现这效果。
4.1、核心实现原理
技术点 | 实现方式 |
去除系统边框 | setWindowFlags(Qt::FramelessWindowHint) |
支持透明背景 | setAttribute(Qt::WA_TranslucentBackground) |
实现圆角 | 在 contentWidget 上设置 border-radius 样式 |
实现阴影 | 使用 QGraphicsDropShadowEffect 添加到内容控件 |
避免锯齿 | 使用 QPainterPath 绘制抗锯齿背景(可选增强) |
4.2、代码实现
CustomWindow.h
#pragma once
#include <QtWidgets/QWidget>
class CustomWindow : public QWidget
{Q_OBJECT
public:explicit CustomWindow(QWidget* parent = nullptr);~CustomWindow();
protected:void paintEvent(QPaintEvent* event) override;
private:void initUI(); // 初始化UIQWidget* contentWidget; // 主内容区域(圆角+阴影载体)
};
这里定义了 CustomWindow 类,继承自 QWidget。public 里是构造和析构函数;protected 重载了 paintEvent 事件,后面绘图要用;private 里有个 contentWidget 指针当主内容区,还有个 initUI 方法用来初始化界面。
CustomWindow.cpp
#include "CustomWindow.h"
#include <QGraphicsDropShadowEffect>
#include <QPainter>
#include <QPainterPath>
#include <QVBoxLayout>
#include <QLabel>
CustomWindow::CustomWindow(QWidget* parent): QWidget(parent)
{// 设置无边框窗口setWindowFlags(Qt::FramelessWindowHint | Qt::Window);// 启用透明背景(关键!)setAttribute(Qt::WA_TranslucentBackground);// 初始大小resize(600, 400);// 初始化界面initUI();
}
构造函数里,先设了无边框窗口的标志,然后 setAttribute (Qt::WA_TranslucentBackground) 是开启透明背景,这样后面的阴影和圆角才能正常显示。
resize 把窗口初始大小设为 600x400,最后调用 initUI 初始化界面。
void CustomWindow::initUI() {contentWidget = new QWidget(this);contentWidget->setObjectName("contentWidget");contentWidget->setStyleSheet("#contentWidget {"" background-color: white;"" border-radius: 10px;"" border: 1px solid #E0E0E0;""}");
initUI 方法里,先 new 了个 contentWidget。给它设了个对象名 “contentWidget”,后面写样式表好用。样式表里指定了 contentWidget 的背景是白色,边框圆角 10 像素,还有个 1 像素的浅灰色边框,这样圆角效果就有了。
// 添加阴影QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect(this);shadow->setBlurRadius(20);shadow->setOffset(0, 0);shadow->setColor(QColor(0, 0, 0, 80));contentWidget->setGraphicsEffect(shadow);
这部分是加阴影。new 了个 QGraphicsDropShadowEffect 对象,setBlurRadius (20) 是说阴影的模糊半径 20 像素,看着更柔和;setOffset (0,0) 是阴影偏移量,这儿设成不偏移;setColor 是阴影颜色,用了半透明的黑色。最后把这阴影效果设给 contentWidget。
auto label = new QLabel("窗口阴影与圆角", contentWidget);label->setAlignment(Qt::AlignCenter);label->setStyleSheet("font-size: 24px;");// 布局QVBoxLayout* layout = new QVBoxLayout(contentWidget);layout->addWidget(label);QVBoxLayout* mainLayout = new QVBoxLayout(this);mainLayout->setContentsMargins(10, 10, 10, 10); // 阴影边距mainLayout->addWidget(contentWidget);
}
这里建了个标签 label,显示 “窗口阴影与圆角”,设成居中对齐,字体大小 24 像素。然后用 QVBoxLayout 给 contentWidget 搞了布局,把 label 加进去。外面又弄了个 mainLayout 当整个窗口的布局,setContentsMargins 设了 10 像素的边距,给阴影留地方,最后把 contentWidget 加进去。
void CustomWindow::paintEvent(QPaintEvent* event) {// 绘制透明背景QPainter painter(this);painter.fillRect(rect(), Qt::transparent);
}
paintEvent 里用 QPainter 把窗口背景画成透明的,避免出现不该有的底色,保证阴影能正常显示。
CustomWindow::~CustomWindow()
{}
析构函数就空着,没啥特殊要处理的。
运行效果
总结
通过以上实践,我们构建了一个功能完备、结构清晰、视觉现代的 Qt 无边框窗口框架。它不仅突破了传统 Qt 窗口的样式限制,也为开发专业级桌面应用提供了坚实的基础。
该方案适用于登录界面、主程序窗口、弹窗、设置面板等多种场景,具备良好的工程价值和扩展潜力。
往期推荐
手撕线程池:C++程序员的能力试金石
打破认知:Linux管道到底有多快?
C++的三种参数传递机制:从底层原理到实战
顺时针螺旋移动法 | 彻底弄懂复杂C/C++嵌套声明、const常量声明!!!
阿里面试官:千万级订单表新增字段,你会怎么弄?
C++内存模型实例解析
字节跳动2面:为了性能,你会牺牲数据库三范式吗?
字节C++一面:enum和enum class的区别?
Redis分布式锁:C++高并发开发的必修课