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 执…

【3】Head First Java 学习笔记

HeadFirst Java 本人有C语言基础&#xff0c;通过阅读Java廖雪峰网站&#xff0c;简单速成了java&#xff0c;但对其中一些入门概念有所疏漏&#xff0c;阅读本书以弥补。 第一章 Java入门 第二章 面向对象 第三章 变量 变量的类型 primitive 主数据引用 变量 primitive主…

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;集合内点不相连 匹配 一个匹配就是一个边的…

彻底解决SimpleDateFormat的线程不安全问题

重现SimpleDateFormat类的线程安全问题 在Java中&#xff0c;SimpleDateFormat是一个非常常用的类&#xff0c;它用于将日期转换成需要的格式或者将文本日期转换为Date对象。然而&#xff0c;在多线程环境下使用SimpleDateFormat可能会遇到一些意想不到的问题。下面通过一个例…

CentOS7配置NFS文件共享

环境准备 准备3个linux服务器&#xff1a; 192.168.137.120&#xff08;nfs server端&#xff09; 192.168.137.121 192.168.137.122 安装nfs-utils工具 # 在3个节点上都执行 yum install nfs-utils -y服务端配置 # 在120上执行 systemctl enable nfs-server systemctl sta…

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; ⭐️ 脚本下载地址&…

Scala OOP

前序 1、Scala 为纯粹OOP1.1、不支持基本类型&#xff1a;一切皆为对象 Byte,Int,... 1.2、不支持静态关键字&#xff1a;static1.3、支持类型推断&#xff0c;和类型预定&#xff0c;动静结合 类 /*关键字&#xff1a;class创建对象&#xff1a;new内含&#xff1a;成员变量…

linux下查看网络路由信息,找到请求过多的IP

netstat -nat|awk {print} |awk -F : {print$1} |sort|uniq -c |sort -n 命令netstat -nat|awk {print} |awk -F : {print$1} |sort|uniq -c |sort -n是用于分析网络连接状态的管道命令序列。下面逐个解释每个命令及其作用&#xff1a; netstat -nat: netstat 是一个用于显示…

代理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;自从关注了誉天的…

初级银行从业资格证知识点(二)

系统内联行清算包括&#xff1a; 全国联行往来、分行辖内往来、支行辖内往来 普通支票既可以支取现金也可以转账&#xff1b;划线支票只能转账不能取现。 进出口业务中采用的结算方式&#xff1a; 汇款、托收、信用证 银行常见的清算模式&#xff1a; 全额清算、净额批量清算、…

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

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