C++ Qt QMainWindow实现无边框窗口自定义标题栏可拖拽移动拉伸改变窗口大小

本篇博客介绍C++ Qt QMainWindow实现无边框窗口,适用于win10/win11系统。

QMainWindow相对于QWidget多了dockedwidget功能,跟多人可能更喜欢用QMainWindow做主窗口,如果不需要dockedwidget功能,QMainWindow与QWidget做主窗口基本无差别。

效果图如下:
在这里插入图片描述
自带窗口阴影、圆角、可拉伸,拖拽。

具体实现过程如下:

一、编写无边框窗口基类CFramelessWindowBase

CFramelessWindowBase.h

/*QMainWindow无边框窗口基类可拉伸其它QMainWindow窗口派生于该类即可*/#pragma once
#include <QMainWindow>class CFramelessWindowBase : public QMainWindow
{
public:CFramelessWindowBase(QWidget* parent = nullptr);~CFramelessWindowBase();protected:bool nativeEvent(const QByteArray& eventType, void* message, long* result) override;private:int mouse_margin = 5;
};

CFramelessWindowBase.cpp

#include "CFramelessWindowBase.h"
#include <qt_windows.h>
#include <windowsx.h>
#include <QWindow>
#include <windows.h>
#include <dwmapi.h>#pragma comment(lib, "dwmapi.lib")CFramelessWindowBase::CFramelessWindowBase(QWidget* parent): QMainWindow(parent)
{setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);setAttribute(Qt::WA_Hover);// 添加窗口阴影,窗口圆角HWND hWnd = reinterpret_cast<HWND>(winId());DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED;::DwmSetWindowAttribute(hWnd, DWMWA_NCRENDERING_POLICY, &ncrp, sizeof(ncrp));MARGINS shadow = { 1, 1, 1, 1 };DwmExtendFrameIntoClientArea((HWND)winId(), &shadow);
}CFramelessWindowBase::~CFramelessWindowBase()
{
}bool CFramelessWindowBase::nativeEvent(const QByteArray& eventType, void* message, long* result)
{MSG* msg = static_cast<MSG*>(message);switch (msg->message){case WM_NCHITTEST:{QPoint globalPos = QCursor::pos();int x = globalPos.x();int y = globalPos.y();//int nX = GET_X_LPARAM(param->lParam) - this->geometry().x();   // bug : windows 在高分屏下,坐标值不正确//int nY = GET_Y_LPARAM(param->lParam) - this->geometry().y();int nX = x - this->geometry().x();int nY = y - this->geometry().y();// 如果鼠标位于内部子控件上,则不进行处理if (nX > mouse_margin && nX < width() - mouse_margin &&nY > mouse_margin && nY < this->height() - mouse_margin){if (childAt(nX, nY) != nullptr)return QWidget::nativeEvent(eventType, message, result);}// 鼠标区域位于窗体边框,进行缩放if ((nX > 0) && (nX < mouse_margin))*result = HTLEFT;if ((nX > this->width() - mouse_margin) && (nX < this->width()))*result = HTRIGHT;if ((nY > 0) && (nY < mouse_margin))*result = HTTOP;if ((nY > this->height() - mouse_margin) && (nY < this->height()))*result = HTBOTTOM;if ((nX > 0) && (nX < mouse_margin) && (nY > 0)&& (nY < mouse_margin))*result = HTTOPLEFT;if ((nX > this->width() - mouse_margin) && (nX < this->width())&& (nY > 0) && (nY < mouse_margin))*result = HTTOPRIGHT;if ((nX > 0) && (nX < mouse_margin)&& (nY > this->height() - mouse_margin) && (nY < this->height()))*result = HTBOTTOMLEFT;if ((nX > this->width() - mouse_margin) && (nX < this->width())&& (nY > this->height() - mouse_margin) && (nY < this->height()))*result = HTBOTTOMRIGHT;return true;}}return QWidget::nativeEvent(eventType, message, result);
}

代码解释:
(1)在CFramelessWindowBase类设置窗口标志,去掉窗口边框,设置最大最小显示效果。

setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);

(2)增加windows窗口阴影与圆角:

// 添加窗口阴影,窗口圆角
HWND hWnd = reinterpret_cast<HWND>(winId());
DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED;
::DwmSetWindowAttribute(hWnd, DWMWA_NCRENDERING_POLICY, &ncrp, sizeof(ncrp));
MARGINS shadow = { 1, 1, 1, 1 };
DwmExtendFrameIntoClientArea((HWND)winId(), &shadow);

这里使用的是DWM API实现窗口阴影和圆角,圆角是windows窗口的圆角,不需要手动设置圆角大小。
(3)重写nativeEvent实现无边框窗口

bool CFramelessWindowBase::nativeEvent(const QByteArray& eventType, void* message, long* result)
{MSG* msg = static_cast<MSG*>(message);switch (msg->message){case WM_NCHITTEST:{QPoint globalPos = QCursor::pos();int x = globalPos.x();int y = globalPos.y();//int nX = GET_X_LPARAM(param->lParam) - this->geometry().x();   // bug : windows 在高分屏下,坐标值不正确//int nY = GET_Y_LPARAM(param->lParam) - this->geometry().y();int nX = x - this->geometry().x();int nY = y - this->geometry().y();// 如果鼠标位于内部子控件上,则不进行处理if (nX > mouse_margin && nX < width() - mouse_margin &&nY > mouse_margin && nY < this->height() - mouse_margin){if (childAt(nX, nY) != nullptr)return QWidget::nativeEvent(eventType, message, result);}// 鼠标区域位于窗体边框,进行缩放if ((nX > 0) && (nX < mouse_margin))*result = HTLEFT;if ((nX > this->width() - mouse_margin) && (nX < this->width()))*result = HTRIGHT;if ((nY > 0) && (nY < mouse_margin))*result = HTTOP;if ((nY > this->height() - mouse_margin) && (nY < this->height()))*result = HTBOTTOM;if ((nX > 0) && (nX < mouse_margin) && (nY > 0)&& (nY < mouse_margin))*result = HTTOPLEFT;if ((nX > this->width() - mouse_margin) && (nX < this->width())&& (nY > 0) && (nY < mouse_margin))*result = HTTOPRIGHT;if ((nX > 0) && (nX < mouse_margin)&& (nY > this->height() - mouse_margin) && (nY < this->height()))*result = HTBOTTOMLEFT;if ((nX > this->width() - mouse_margin) && (nX < this->width())&& (nY > this->height() - mouse_margin) && (nY < this->height()))*result = HTBOTTOMRIGHT;return true;}}return QWidget::nativeEvent(eventType, message, result);
}

二、实现主窗口
派生于上面的CFramelessWindowBase,代码如下:
FramelessWindow.h

#pragma once#include <QtWidgets/QMainWindow>
#include "CFramelessWindowBase.h"
#include "TitleBar.h"
#include "ContentWidget.h"
#include "LeftBar.h"
#include "CustomStatusBar.h"class FramelessWindow : public CFramelessWindowBase
{Q_OBJECTpublic:FramelessWindow(QWidget *parent = nullptr);~FramelessWindow();private slots:void OnClose();private:TitleBar* m_pTitleBar = nullptr;ContentWidget* m_pContentWidget = nullptr;LeftBar* m_pLeftBar = nullptr;CustomStatusBar* m_pStatusBar = nullptr;
};

FramelessWindow.cpp

/*主窗口*/#include "FramelessWindow.h"
#include <QVBoxLayout>
#include <QMessageBox>FramelessWindow::FramelessWindow(QWidget *parent): CFramelessWindowBase(parent)
{this->resize(800, 600);QWidget* pWidget = new QWidget(this);this->setCentralWidget(pWidget);m_pTitleBar = new TitleBar(pWidget);m_pTitleBar->SetTitleText(tr("QMainWindow Custom Title"));QString logo_qss = R"(QLabel{background-image:url(:/TitleBar/Resources/TitleBar/logo32.svg);background-position:center; background-repeat: no-repeat;border:none})";m_pTitleBar->SetTitleIcon(logo_qss);m_pContentWidget = new ContentWidget(pWidget);m_pLeftBar = new LeftBar(pWidget);m_pStatusBar = new CustomStatusBar(pWidget);QVBoxLayout* pVLay = new QVBoxLayout(pWidget);pVLay->setSpacing(0);pVLay->setContentsMargins(0, 0, 0, 0);pVLay->addWidget(m_pTitleBar);QHBoxLayout* pHLay = new QHBoxLayout(pWidget);pHLay->setSpacing(0);pHLay->addWidget(m_pLeftBar);pHLay->addWidget(m_pContentWidget);pVLay->addLayout(pHLay);pVLay->addWidget(m_pStatusBar);pWidget->setLayout(pVLay);connect(m_pTitleBar, &TitleBar::sig_Close, this, &FramelessWindow::OnClose);
}FramelessWindow::~FramelessWindow()
{
}void FramelessWindow::OnClose()
{QMessageBox::StandardButton resBtn = QMessageBox::question(this, tr("Tips"),tr("Are you sure you want to close the window?"),QMessageBox::Cancel | QMessageBox::Yes,QMessageBox::Yes);if (resBtn == QMessageBox::Yes) {close();}
}

本篇博客源码下载:
https://download.csdn.net/download/yao_hou/89211306?spm=1001.2014.3001.5501

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/828387.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB实现蚁群算法栅格路径优化

蚁群算法是一种模拟自然界中蚂蚁觅食行为的优化算法&#xff0c;常用于解决路径规划问题。在栅格路径优化中&#xff0c;蚁群算法可以帮助找到从起点到终点的最优路径。以下是蚁群算法栅格路径优化的基本流程步骤&#xff1a; 初始化参数&#xff1a; (1)设置蚂蚁数量&#xff…

linux的“>”和“>>”

在Linux中&#xff0c;>和>>都是用于文件重定向的操作符&#xff0c;它们用于将命令的输出发送到文件中。 > 用于创建一个新文件或覆盖现有文件的内容。当你执行一个如 command > file.txt 的命令时&#xff0c;如果 file.txt 文件存在&#xff0c;它的内容将被…

2024最新版JavaScript逆向爬虫教程-------基础篇之深入JavaScript运行原理以及内存管理

目录 一、JavaScript运行原理1.1 前端需要掌握的三大技术1.2 为什么要学习JavaScript1.3 浏览器的工作原理1.4 浏览器的内核1.5 浏览器渲染过程1.6 认识JavaScript引擎1.7 V8引擎以及JavaScript的执行过程1.8 V8引擎执行过程 二、JavaScript的执行过程2.1 初始化全局对象2.2 执…

OriginPro作图之箱线图

前言 箱线图(Box-plot) 又称为盒须图、盒式图或箱线图&#xff0c;是一种用作显示一组数据分散情况资料的统计图。因型状如箱子而得名。 本文将结合实例阐述其意义和绘图过程。 箱线图简介 箱线图(Boxplot) 也称箱须图( Box-whisker Plot)&#xff0c;是利用数据中的五个统计量…

ffmpeg的安装以及使用

1.FFmpeg 的主要功能和特性&#xff1a; 格式转换&#xff1a;FFmpeg 可以将一个媒体文件从一种格式转换为另一种格式&#xff0c;支持几乎所有常见的音频和视频格式&#xff0c;包括 MP4、AVI、MKV、MOV、FLV、MP3、AAC 等。视频处理&#xff1a;FFmpeg 可以进行视频编码、解…

ArcGIS无法开始编辑TIN!开始编辑TIN显示灰色

ArcGIS无法开始编辑TIN&#xff01;开始编辑TIN显示灰色&#xff1f; 解决方案&#xff01; 1、确认自定义——扩展模块中空间分析、3D分析模块勾选。 2、确认以上后&#xff0c;还是不能编辑的话&#xff0c;我们可以调出 3D分析分析工具条&#xff0c;你就会发现。TIN编辑工…

Window + Ubuntu 双系统无Ubuntu Bios 启动项

文章目录 安装硬盘位置不重要&#xff01;&#xff01;&#xff01;&#xff08;但是我安装在了第二张HDD&#xff09;问题是多盘分位置会导致磁盘主分区变成了简单卷 Bios Ubuntu 启动项修复参考Ubuntu安装U盘进入Try Ubuntu 使用Terminal修复完提示Disable Secure Boot进入Te…

【存储】cosbench对象存储测试工具

目录 简略说明 原理 用法 详细说明 简介 用法 一 安装 二 简单验证 三 编写配置文件 四 提交配置文件下IO 五 测试结果查看 结果概览 查看详情 每秒钟的io情况查看 工作负载配置 参数配置&#xff08;controller和driver&#xff09; 查看错误的方法和错误记录 查看错误的方法 …

【匹配】匈牙利匹配算法

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 匈牙利匹配算法 1. 正文 1.1 基础概念 二分图 顶点分为两个集合&#xff0c;集合间顶点相连&#xff0c;集合内点不相连 匹配 一个匹配就是一个边的…

Oracle Linux 8.8 一键安装 Oracle 11GR2 RAC(231017)

前言 Oracle 一键安装脚本&#xff0c;演示 Oracle Linux 8.8 一键安装 Oracle 11GR2 RAC&#xff08;231017&#xff09;过程&#xff08;全程无需人工干预&#xff09;&#xff1a;&#xff08;脚本包括 ORALCE PSU/OJVM 等补丁自动安装&#xff09; ⭐️ 脚本下载地址&…

代理IP干货:如何正确使用防范风险?

在今天的数字时代&#xff0c;代理IP地址已成为互联网世界中不可或缺的一部分。无论您是寻求绕过地理限制、保护个人隐私还是执行网络任务&#xff0c;代理IP地址都发挥着关键作用。我们将为您探讨代理IP地址的重要性以及如何防范潜在的风险和威胁。 一、代理IP地址的潜在风险 …

STM32H7独立看门狗 (IWDG)的应用方法介绍

目录 概述 1 认识独立看门狗 (IWDG) 1.1 定义独立看门狗 (IWDG) 1.2 IWDG 主要特性 2 IWDG 功能说明 2.1 IWDG 框图 2.2 IWDG 内部信号 2.3 窗口选项 2.3.1 Enable WIN IWDG 2.3.2 Disable WIN IWDG 2.4 硬件看门狗 2.5 低功耗冻结 2.6 停止和待机模式下的行为 …

网工学习云计算HCIE感受如何?

作为一名网工&#xff0c;我经常会在各种网络论坛里查询搜索一些网络技术资料&#xff0c;以及跟论坛里的网友交流讨论平时在工作、学习中遇到的问题、故障&#xff0c;因此也经常能在论坛的首页看到誉天的宣传信息。机缘巧合之下关注了誉天的B站号&#xff0c;自从关注了誉天的…

实现像 creat-astro 一样在终端中实现动态输出内容

新工具&#xff0c;可以动态输出一些文字&#xff0c;支持盒子输出、动物输出、emoji输出等&#xff0c;也可以完全自定义 可以参考地址&#xff1a;https://github.com/winchesHe/dynamic-log 演示&#xff1a;

谈谈对“数字化转型”的本质认知

我之前在多家咨询公司与软件公司做过多个大型企业数字化项目&#xff0c;也在甲方企业推动数字化转型&#xff0c;做出了数字化最佳实践案例。 下面我想从一个客观角度来真正意义上的描述数字化及数字化转型。 我相信这篇文章能给做数字化的你们带来极大的收获&#xff0c;我…

了解血糖对身体的危害,掌握三个关键数值,预防并发症。

糖尿病患者的血糖控制至关重要&#xff0c;因为长期的血糖异常会对身体造成各种损害&#xff0c;甚至引发严重的并发症。记住这三个数值。 第一个就是空腹血糖&#xff0c;大于13.9&#xff0c;就会有大量的脂肪分解成酮体&#xff0c;酮体在体内积累过多&#xff0c;可能引发酮…

第55篇:创建Nios II工程之Hello_World<一>

Q&#xff1a;本期我们开始介绍创建Platform Designer系统&#xff0c;并设计基于Nios II Processor的Hello_world工程。 A&#xff1a;设计流程和实验原理&#xff1a;需要用到的IP组件有Clock Source、Nios II Processor、On-Chip Memory、JTAG UART和System ID外设。Nios I…

内网穿透下的 wordpress 地址冲突问题与 https 下的后台登陆问题

内网穿透下的 wordpress 地址冲突问题与 https 下的后台登陆问题 内网穿透下的地址冲突https 登录管理后台总结 同步发布在个人笔记内网穿透下的 wordpress 地址冲突问题与 https 下的后台登陆问题 笔记记录解决两个 wordpress 相关问题 如果我们使用内网穿透把本地的 wordpre…

机器学习理论基础—支持向量机的推导(一)

机器学习理论基础—支持向量机的推导 算法原理 SVM:从几何角度&#xff0c;对于线性可分数据集&#xff0c;支持向量机就是找距离正负样本都最远的超平面&#xff0c;相比于感知机&#xff0c;其解是唯一的&#xff0c;且不偏不倚&#xff0c;泛化性能更好。 超平面 n维空间…

百篇博客 · 千里之行

时光荏苒流逝&#xff0c;白驹匆匆过隙&#xff0c;不知不觉间&#xff0c;Damon小智已经在CSDN上记录了第一百多篇文章。恰逢128天创作纪念日的此刻&#xff0c;我感慨良多&#xff0c;这百余篇博客不仅是我的创作历程&#xff0c;更见证了我在这五年技术生涯中走过心路历程。…