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

news/2024/10/21 9:48:26/

本篇博客介绍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.ppmy.cn/news/1435710.html

相关文章

数据仓库与数据挖掘(实验一2024.4.24)

实验准备&#xff1a; 1.下载conda 2.配置环境C:\ProgramData\miniconda3\Scripts 3.创建文件夹panda进入虚拟环境qq 激活虚拟环境&#xff1a;activate qq 启动jupyter lab&#xff08;python语言环境编译&#xff09;&#xff1a;jupyter lab 4.panda下载 &#xff08;…

面试——数据库中的锁升级(Lock Escalation)机制

假设执行下面的sql语句 update tb_user set age age 1;以MySQL为例&#xff0c;按照两阶段锁协议&#xff0c;会先给tb_user加上表意向锁&#xff0c;然后对tb_user的所有行加上行锁&#xff0c;但是当tb_user数据量非常多的时候&#xff0c;频繁的获取行锁会影响性能&#…

excel 按照姓名日期年月分组求和

excel 需要按照 姓名 日期的年份进行金额求和统计&#xff0c;采用sumifs 进行统计 注意&#xff1a;sumifs 不支持 合并列拆分计算&#xff0c;合并列只会计算一个值 表格数据大概如下&#xff1a;(sheet) ABC姓名日期金额A2023/01/01500A2023/01/151500B2023/01/01200B202…

线性代数-知识点复习(面试用)

整理&#xff1a;Peter1146717850 一、向量与线性组合 向量&#xff1a;往什么方向走多么远 e.g. ( 1 2 ) \begin{pmatrix} 1 \\ 2\end{pmatrix} (12​) 向量的模&#xff1a;向量的长度 向量的加减法&#xff1a;向量对应元素相加减&#xff08;前提&#xff1a;维度相同…

动态规划---斐波那契数列模型

目录 一、斐波那契数列的基本概念 二、动态规划在斐波那契数列中的应用与优势 三、实际案例&#xff1a;使用动态规划解决斐波那契数列问题 四、动态规划问题的做题步骤 五、例题 1、第N个泰波那契数---点击跳转题目 2、三步问题----点击跳转题目 3、最小花费爬楼梯---…

文件摆渡:安全、高效的摆渡系统助力提升效率

很多组织和企业都会通过网络隔离的方式来保护内部的数据&#xff0c;网络隔离可以是物理隔离&#xff0c;也可以是逻辑隔离&#xff0c;如使用防火墙、VPN、DMZ等技术手段来实现&#xff0c;隔离之后还会去寻找文件摆渡方式&#xff0c;来保障日常的业务和经营需求。 进行网络隔…

【数据结构】串(String)

文章目录 基本概念顺序存储结构比较当前串与串s的大小取子串插入删除其他构造函数拷贝构造函数扩大数组空间。重载重载重载重载[]重载>>重载<< 链式存储结构链式存储结构链块存储结构 模式匹配朴素的模式匹配算法(BF算法)KMP算法字符串的前缀、后缀和部分匹配值nex…

C++ | Leetcode C++题解之第48题旋转图像

题目&#xff1a; 题解&#xff1a; class Solution { public:void rotate(vector<vector<int>>& matrix) {int n matrix.size();// 水平翻转for (int i 0; i < n / 2; i) {for (int j 0; j < n; j) {swap(matrix[i][j], matrix[n - i - 1][j]);}}//…