Qt无边框窗口如何保留操作按钮并设置圆角?
实现带自定义操作按钮的圆角无边框Qt窗口
嘿,我之前做Qt项目的时候也碰到过一模一样的问题!用setWindowFlags(Qt::Window | Qt::FramelessWindowHint)之后,系统自带的标题栏、操作按钮全没了,还要自己搞定圆角窗口和自定义按钮,对吧?别慌,我给你一步步拆解怎么实现:
1. 先搞定圆角窗口
去掉系统边框后,Qt默认不会渲染圆角,得自己动手。这里有两种靠谱的实现方式:
方法一:重写paintEvent绘制圆角
这种方法灵活性更高,适合需要复杂绘制的场景:
#include <QPainter> #include <QPainterPath> void YourMainWindow::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿,让圆角更平滑 // 绘制圆角路径,参数12是圆角半径,按需调整 QPainterPath path; path.addRoundedRect(this->rect(), 12, 12); // 设置裁剪区域,超出圆角的部分会被切掉 painter.setClipPath(path); // 调用父类的paintEvent,保证窗口内容正常绘制 QMainWindow::paintEvent(event); }
方法二:用样式表+透明背景
这种方法更简单,适合快速实现:
首先给窗口设置透明背景属性(必须配合无边框使用):
this->setAttribute(Qt::WA_TranslucentBackground);
然后给窗口添加样式表(注意指定窗口的类名或objectName,避免影响子控件):
QMainWindow#YourMainWindow { background-color: #ffffff; /* 设置窗口背景色 */ border-radius: 12px; /* 圆角半径 */ }
小提示:如果你的窗口是QWidget而不是QMainWindow,把选择器改成
QWidget#YourWidget就行。另外要确保子控件背景设为透明,避免覆盖窗口圆角效果。
2. 添加自定义窗口操作按钮
系统标题栏没了,我们得自己做最小化、最大化/还原、关闭这三个按钮:
步骤1:创建按钮并关联槽函数
#include <QPushButton> #include <QHBoxLayout> // 把这个初始化函数放在窗口构造函数里调用 void YourMainWindow::initTitleBarButtons() { // 最小化按钮 QPushButton* minBtn = new QPushButton(this); minBtn->setIcon(QIcon(":/icons/minimize.svg")); // 替换成你的图标路径,也可以用文字"-" minBtn->setFixedSize(30, 30); // 设置按钮大小 connect(minBtn, &QPushButton::clicked, this, &QMainWindow::showMinimized); // 最大化/还原按钮 QPushButton* maxBtn = new QPushButton(this); maxBtn->setIcon(QIcon(":/icons/maximize.svg")); maxBtn->setFixedSize(30, 30); connect(maxBtn, &QPushButton::clicked, [this, maxBtn](){ if (this->isMaximized()) { this->showNormal(); maxBtn->setIcon(QIcon(":/icons/restore.svg")); } else { this->showMaximized(); maxBtn->setIcon(QIcon(":/icons/maximize.svg")); } }); // 关闭按钮 QPushButton* closeBtn = new QPushButton(this); closeBtn->setObjectName("closeBtn"); // 给按钮设置唯一标识,方便样式表匹配 closeBtn->setIcon(QIcon(":/icons/close.svg")); closeBtn->setFixedSize(30, 30); connect(closeBtn, &QPushButton::clicked, this, &QMainWindow::close); // 把按钮放到水平布局里 QHBoxLayout* btnLayout = new QHBoxLayout(); btnLayout->addWidget(minBtn); btnLayout->addWidget(maxBtn); btnLayout->addWidget(closeBtn); btnLayout->setSpacing(0); btnLayout->setContentsMargins(0, 0, 10, 0); // 右边留10px边距 // 搭建标题栏布局,把按钮挤到右上角 QWidget* titleBar = new QWidget(this); QHBoxLayout* titleBarLayout = new QHBoxLayout(titleBar); titleBarLayout->addStretch(); // 左侧拉伸占位 titleBarLayout->addLayout(btnLayout); titleBarLayout->setContentsMargins(0, 0, 0, 0); titleBar->setFixedHeight(30); // 设置标题栏高度 // 把标题栏放到主窗口顶部 this->setMenuWidget(titleBar); }
步骤2:美化按钮样式
给按钮加hover效果,提升交互体验,添加样式表:
QPushButton { border: none; background-color: transparent; icon-size: 16px; /* 调整图标大小 */ } QPushButton:hover { background-color: #f5f5f5; border-radius: 4px; /* 按钮hover时的圆角 */ } QPushButton#closeBtn:hover { background-color: #ff4c4c; /* 如果用SVG图标,可以设置hover时的颜色 */ filter: invert(1) brightness(2); }
3. 补充:实现窗口拖拽功能
无边框窗口默认不能拖拽移动,我们自己实现这个功能:
// 在窗口类的头文件里添加私有成员变量 private: QPoint m_dragStartPos; // 重写鼠标事件 void YourMainWindow::mousePressEvent(QMouseEvent *event) { // 只有点击标题栏区域(y坐标小于标题栏高度)才允许拖拽 if (event->button() == Qt::LeftButton && event->pos().y() <= 30) { m_dragStartPos = event->globalPos() - this->frameGeometry().topLeft(); event->accept(); } } void YourMainWindow::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { this->move(event->globalPos() - m_dragStartPos); event->accept(); } }
这样用户点击标题栏区域就能拖拽窗口,避免整个窗口都能拖动的尴尬。
最后注意事项
- 如果用了
Qt::WA_TranslucentBackground,要确保窗口没有设置Qt::WA_OpaquePaintEvent属性,否则透明效果会失效。 - 最大化窗口时,圆角会被屏幕边缘覆盖,这是正常现象;如果想最大化时也保留圆角,可以在
showMaximized时调整窗口大小,给屏幕边缘留一点空隙。
内容的提问来源于stack exchange,提问作者Hhry




