蓝布编程网

分享编程技术文章,编程语言教程与实战经验

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);

}

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言