Qt自绘无边框窗口类

概述:

功能: 无边框窗体基类,支持最小化、最大化、关闭、鼠标拖拽、鼠标按下移动等;
文件说明:
1.CFrameWidget.h 无边框窗体基类
2.CTitle.h 窗体上方标题栏类
3.TextWidget.h 测试窗体类

代码示例:

CFrameWidget.h:

/*
 *  @file	:CFrameWidget.h
 *	@note 	:无边框窗体基类,支持最小化、最大化、关闭、鼠标拖拽、鼠标按下移动等
 *	@author :Fantast
 *	@data	:2021-5-10
 */
#pragma once
#include <QWidget>
#include <QFile>
#include <QMoveEvent>

class CFrameWidget : public QWidget
{
	Q_OBJECT

public:
	CFrameWidget(QWidget *parent);
	~CFrameWidget();

	/*
	 *	@brief	:设置样式
	 *	@qssPath:路径
	 */
	void setStyleSheet(const QString qssPath);

	/*
	 *	@brief 设置是否可以移动
	 */
	void setMoveAble(bool enable);

	/*
	*	@brief 设置是否可以缩放
	*/
	void setZoomAble(bool enable);

	/*
	 *	@brief 设置可拖动区域高(title高)
	 */
	void setTitleHeight(const int height);
public slots:
	/*
	 *	@brief 窗口关闭
	 */
	void slot_CloseWidget();
	/*
	 *	@brief 窗口缩放
	 */
	void slot_MaxWidget();
	/*
	 *	@brief 窗口最小化
	 */
	void slot_MinWidget();

private:
	/*
	*	@brief 检测鼠标是否在边缘,显示拖动鼠标样式
	*/
	void checkEdge();

protected:
	/*
	 *	@brief 鼠标双击事件
	 */
	virtual void mouseDoubleClickEvent(QMouseEvent *event);
	/*
	 *	@brief 鼠标按下事件
	 */
	virtual void mousePressEvent(QMouseEvent * event);
	/*
	 *	@brief 鼠标抬起事件
	 */
	virtual void mouseReleaseEvent(QMouseEvent * event);
	/*
	 *	@brief 鼠标移动事件
	 */
	virtual void mouseMoveEvent(QMouseEvent * event);
	/*
	 *	@brief 设置从最小化放大后正常处理事件
	 */
	void showEvent(QShowEvent * e);

private:
	// 鼠标上次移动开始时相对屏幕的位置
	QPoint mPntStart;
	// 鼠标是否在可拖动位置按下
	bool mousePressInMoveArea;
	// 鼠标是否在拖拽大小的位置按下
	bool mousePressBorderArea;
	// 鼠标是否持续按下
	bool mbKeepPressed;
	//窗口是否可缩放
	bool allowZoom;
	//窗口是否可移动
	bool allowMove;
	//鼠标检测的边缘距离
	int m_nEdgeMargin;
	//窗口拖动距离范围
	int TitlearHeight;
	//更改窗口大小的方向
	typedef enum
	{
		NO_DIR = 0x00,
		TOP = 0x01,
		BOTTOM = 0x02,
		LEFT = 0x04,
		RIGHT = 0x08,
		TOP_LEFT = 0x01 | 0x04,
		TOP_RIGHT = 0x01 | 0x08,
		BOTTOM_LEFT = 0x02 | 0x04,
		BOTTOM_RIGHT = 0x02 | 0x08
	} EMouseDir;
	EMouseDir m_nMouseDir;
};

CFrameWidget.cpp

#include "CFrameWidget.h"

#define min(a,b) ((a)<(b)? (a) :(b))
#define max(a,b) ((a)>(b)? (a) :(b))

CFrameWidget::CFrameWidget(QWidget *parent)
	: QWidget(parent)
	, m_nMouseDir(EMouseDir::NO_DIR)
	, mousePressInMoveArea (false)
	, mousePressBorderArea(false)
	, mbKeepPressed(false)
	, allowZoom(true)
	, allowMove(true)
	, m_nEdgeMargin(2)
	, TitlearHeight(20)
{
	setWindowFlags(Qt::FramelessWindowHint);//无边框
	setMouseTracking(true);//鼠标跟踪
}

CFrameWidget::~CFrameWidget()
{
}

void CFrameWidget::setStyleSheet(const QString qssPath)
{
	QFile file(qssPath);
	file.open(QFile::ReadOnly);
	if (file.isOpen())
	{
		QWidget::setStyleSheet(file.readAll());
		file.close();
	}
}

void CFrameWidget::setZoomAble(bool enable) 
{
	allowZoom = enable;
}

void CFrameWidget::setMoveAble(bool enable) 
{
	allowMove = enable;
}

void CFrameWidget::mouseMoveEvent(QMouseEvent *event)
{
	// 持续按住才做对应事件
	if (mousePressInMoveArea)
	{
		//如果鼠标不是放在边缘那么说明这是在拖动窗口
		if (m_nMouseDir == NO_DIR)
		{
			//将窗体移动到鼠标位置
			move(event->globalPos() - mPntStart);
		}
	}
	//拖拽放大缩小
	if (mousePressBorderArea)
	{
		//窗口上下左右的值
		int nTop, nBottom, nLeft, nRight;
		nTop = frameGeometry().top();
		nBottom = frameGeometry().bottom();
		nLeft = frameGeometry().left();
		nRight = frameGeometry().right();

		//检测更改尺寸方向中包含的上下左右分量
		if (m_nMouseDir & TOP)
		{
			if (height() == minimumHeight())
			{
				nTop = min(event->globalY(), nTop);
			}
			else if (height() == maximumHeight())
			{
				nTop = max(event->globalY(), nTop);
			}
			else
			{
				nTop = event->globalY();
			}
		}
		else if (m_nMouseDir & BOTTOM)
		{
			if (height() == minimumHeight())
			{
				nBottom = max(event->globalY(), nTop);
			}
			else if (height() == maximumHeight())
			{
				nBottom = min(event->globalY(), nTop);
			}
			else
			{
				nBottom = event->globalY();
			}
		}
		if (m_nMouseDir & LEFT)
		{                        //检测左右分量
			if (width() == minimumWidth())
			{
				nLeft = min(event->globalX(), nLeft);
			}
			else if (width() == maximumWidth())
			{
				nLeft = max(event->globalX(), nLeft);
			}
			else
			{
				nLeft = event->globalX();
			}
		}
		else if (m_nMouseDir & RIGHT)
		{
			if (width() == minimumWidth())
			{
				nRight = max(event->globalX(), nRight);
			}
			else if (width() == maximumWidth())
			{
				nRight = min(event->globalX(), nRight);
			}
			else
			{
				nRight = event->globalX();
			}
		}
		setGeometry(QRect(QPoint(nLeft, nTop), QPoint(nRight, nBottom)));
		}
	else if (allowZoom&&!isMaximized())
	{
		checkEdge();
	}
	event->ignore();
}

void CFrameWidget::checkEdge()
{
	//计算鼠标距离窗口上下左右有多少距离
	int nDeltaLeft = abs(cursor().pos().x() - frameGeometry().left());
	int nDeltaRight = abs(cursor().pos().x() - frameGeometry().right());
	int nDeltaTop = abs(cursor().pos().y() - frameGeometry().top());
	int nDeltaBottom = abs(cursor().pos().y() - frameGeometry().bottom());

	QCursor tempCursor = cursor();

	if (nDeltaTop < m_nEdgeMargin)
	{
		//根据 边缘距离 分类改变尺寸的方向
		if (nDeltaLeft < m_nEdgeMargin)
		{
			m_nMouseDir = TOP_LEFT;
			tempCursor.setShape(Qt::SizeFDiagCursor);
		}
		else if (nDeltaRight < m_nEdgeMargin)
		{
			m_nMouseDir = TOP_RIGHT;
			tempCursor.setShape(Qt::SizeBDiagCursor);
		}
		else
		{
			m_nMouseDir = TOP;
			tempCursor.setShape(Qt::SizeVerCursor);
		}
	}
	else if (nDeltaBottom < m_nEdgeMargin)
	{
		if (nDeltaLeft < m_nEdgeMargin)
		{
			m_nMouseDir = BOTTOM_LEFT;
			tempCursor.setShape(Qt::SizeBDiagCursor);
		}
		else if (nDeltaRight < m_nEdgeMargin)
		{
			m_nMouseDir = BOTTOM_RIGHT;
			tempCursor.setShape(Qt::SizeFDiagCursor);
		}
		else
		{
			m_nMouseDir = BOTTOM;
			tempCursor.setShape(Qt::SizeVerCursor);
		}
	}
	else if (nDeltaLeft < m_nEdgeMargin)
	{
		m_nMouseDir = LEFT;
		tempCursor.setShape(Qt::SizeHorCursor);
	}
	else if (nDeltaRight < m_nEdgeMargin)
	{
		m_nMouseDir = RIGHT;
		tempCursor.setShape(Qt::SizeHorCursor);
	}
	else
	{
		m_nMouseDir = NO_DIR;
		tempCursor.setShape(Qt::ArrowCursor);
	}
	//重新设置鼠标样式
	setCursor(tempCursor);
}

void CFrameWidget::mousePressEvent(QMouseEvent *event)
{
	// 鼠标左键按下事件
	if (event->button() == Qt::LeftButton)
	{
		mbKeepPressed = true;
		if (!isMaximized())
		{
			if (m_nMouseDir != NO_DIR&&allowZoom)
			{
				mousePressBorderArea = true;
			}
			else if (allowMove)
			{
				//是否为双击和拖动区域
				int x = event->pos().x();
				int y = event->pos().y();
				if (x >= 0 && x <= this->width()
					&& y >= m_nEdgeMargin/2 && y <= TitlearHeight)
				{
					// 标记鼠标按下
					mousePressInMoveArea = true;
					// 记录鼠标与窗体位置的差值
					mPntStart = event->pos();
				}
			}
		}
	}
}

void CFrameWidget::mouseReleaseEvent(QMouseEvent *event)
{
	if (event->button() == Qt::LeftButton)
	{
		// 鼠标左键释放
		mbKeepPressed = false;
		mousePressBorderArea = false;
		mousePressInMoveArea = false;
	}
}

void CFrameWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
	int x = event->pos().x();
	int y = event->pos().y();
	if (x >= 0 && x <= this->width()
		&& y >= 0 && y <= TitlearHeight)
	{
		slot_MaxWidget();
	}
}

void CFrameWidget::slot_CloseWidget() {
	close();
}

void CFrameWidget::slot_MaxWidget()
{
	if (allowZoom)
	{
		if (isMaximized())
		{
			showNormal();
		}
		else {
			showMaximized();
		}
	}
}

void CFrameWidget::slot_MinWidget()
{
	showMinimized();
}

void CFrameWidget::showEvent(QShowEvent *e)
{
	this->setAttribute(Qt::WA_Mapped);
	QWidget::showEvent(e);
}
void CFrameWidget::setTitleHeight(const int height)
{
	//获取标题栏高度
	TitlearHeight = height;
}

// //调用WIN API需要用到的头文件与库 [实现缩放]
// #ifdef Q_OS_WIN
// #include <qt_windows.h>
// #include <Windowsx.h>
// #endif
// //nativeEvent主要用于进程间通信-消息传递,使用这种方式后来实现窗体的缩放 [加上了这函数,窗口也能移动了]
// bool WidgetBaseClass::nativeEvent(const QByteArray &eventType, void *message, long *result)
// {
// 	int m_nBorderWidth = 5;
// 	Q_UNUSED(eventType)
// 		MSG *param = static_cast<MSG *>(message);
// 	switch (param->message)
// 	{
// 	case WM_NCHITTEST:
// 	{
// 		int nX = GET_X_LPARAM(param->lParam) - this->geometry().x();
// 		int nY = GET_Y_LPARAM(param->lParam) - this->geometry().y();
// 		*result = HTCLIENT;
// 		// 鼠标区域位于标题栏按钮之上,则不进行处理
// 		QList<QPushButton *> buttons = TopBar->findChildren<QPushButton *>();
// 		foreach(QPushButton *pButton, buttons)
// 		{
// 			if (pButton->geometry().contains(QPoint(nX, nY)))
// 			{
// 				return true;
// 			}
// 		}
// 		// 鼠标区域位于标题栏中,进行移动
// 		if (nX >= m_nBorderWidth && nX <= this->width() - m_nBorderWidth
// 			&& nY >= m_nBorderWidth && nY <= TopBar->height())
// 		{
// 			return true;
// 		}
// 
// 		// 鼠标区域位于窗体边框,进行缩放
// 		if ((nX > 0) && (nX < m_nBorderWidth))
// 			*result = HTLEFT;
// 		if ((nX > this->width() - m_nBorderWidth) && (nX < this->width()))
// 			*result = HTRIGHT;
// 		if ((nY > 0) && (nY < m_nBorderWidth))
// 			*result = HTTOP;
// 		if ((nY > this->height() - m_nBorderWidth) && (nY < this->height()))
// 			*result = HTBOTTOM;
// 		if ((nX > 0) && (nX < m_nBorderWidth) && (nY > 0)
// 			&& (nY < m_nBorderWidth))
// 			*result = HTTOPLEFT;
// 		if ((nX > this->width() - m_nBorderWidth) && (nX < this->width())
// 			&& (nY > 0) && (nY < m_nBorderWidth))
// 			*result = HTTOPRIGHT;
// 		if ((nX > 0) && (nX < m_nBorderWidth)
// 			&& (nY > this->height() - m_nBorderWidth) && (nY < this->height()))
// 			*result = HTBOTTOMLEFT;
// 		if ((nX > this->width() - m_nBorderWidth) && (nX < this->width())
// 			&& (nY > this->height() - m_nBorderWidth) && (nY < this->height()))
// 			*result = HTBOTTOMRIGHT;
// 		return true;
// 
// 	}
// 	}
// 	return QWidget::nativeEvent(eventType, message, result);
// }

CTitle.h:

#pragma once

#include <QWidget>
#include "ui_CTitle.h"

class CTitle : public QWidget
{
	Q_OBJECT

public:
	CTitle(QWidget *parent = Q_NULLPTR);
	~CTitle();

private:
	/*
	 *  @brief:初始化操作
	 */
	void Init();

signals:
	/**
	* @note 最小化信号
	*/
	void SignalMinisizeWidget();

	/**
	* @note 还原信号
	*/
	void SignalMaxisizeWidget();

	/**
	* @note 关闭信号
	*/
	void SignalCloseWidget();

protected slots:
	/**
	* @note 最小化窗口的槽函数
	*/
	void SlotMinisizeWidget();

	/**
	* @note 还原窗口的槽函数
	*/
	void SlotMaxisizeWidget();

	/**
	* @note 关闭窗口的槽函数
	*/
	void SlotCloseWidget();
private:
	Ui::CTitle ui;
};

CTitle.cpp:

#include "CTitle.h"

CTitle::CTitle(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);
	//无边框
	setWindowFlags(Qt::FramelessWindowHint);
	Init();
}

CTitle::~CTitle()
{

}

void CTitle::Init()
{
	connect(ui.pushButton_min, &QPushButton::clicked, this, &CTitle::SlotMinisizeWidget);
	connect(ui.pushButton_max, &QPushButton::clicked, this, &CTitle::SlotMaxisizeWidget);
	connect(ui.pushButton_close, &QPushButton::clicked, this, &CTitle::SlotCloseWidget);
}

void CTitle::SlotMinisizeWidget()
{
	emit SignalMinisizeWidget();
}

void CTitle::SlotMaxisizeWidget()
{
	emit SignalMaxisizeWidget();
}

void CTitle::SlotCloseWidget()
{
	emit SignalCloseWidget();
}

CTitle.ui:

在这里插入图片描述

TextWidget.h:

#pragma once

#include <QtWidgets/QWidget>
#include "ui_TextWidget.h"
#include "CFrameWidget.h"

class TextWidget : public CFrameWidget
{
	Q_OBJECT

public:
	TextWidget(QWidget *parent = Q_NULLPTR);

private:
	/*
	 *  @brief:初始化函数
	 */
	void Init();

private:
	Ui::TextWidgetClass ui;
};

TextWidget.cpp:

#include "TextWidget.h"
#include "Title/CTitle.h"
TextWidget::TextWidget(QWidget *parent)
	: CFrameWidget(parent)
{
	ui.setupUi(this);
	Init();
}

void TextWidget::Init()
{
	CTitle *title = new CTitle;
	ui.Layout_title->addWidget(title);
	//连接标题栏信号槽
	connect(title, SIGNAL(SignalMinisizeWidget()), this, SLOT(slot_MinWidget()));
	connect(title, SIGNAL(SignalMaxisizeWidget()), this, SLOT(slot_MaxWidget()));
	connect(title, SIGNAL(SignalCloseWidget()), this, SLOT(slot_CloseWidget()));
	setStyleSheet(":/TextWidget/Resources/Widget.qss");
}


TextWidget.ui:

在这里插入图片描述

运行效果图:

在这里插入图片描述

over:

欢迎大家关注作者在文末评论、点赞、转发以及批评指正!
如果大家有更好的方法或有问题可以在文末评论一起讨论!
共同学习!
共同进步!

文末一句话:

因为这些无论好坏的碎片拼凑,才组成我们琐碎又完整的生活。
因为这些喜怒哀乐的微小情绪,才是我们最真实最纯粹的样子。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值