Bootstrap

第一章 Qt自定义控件——标题栏

在企业开发中,当做一款软件时,肯定会接触到标题栏的设计。在Qt中QWidget以及QMainWindow或者QDialog虽然有自带的标题栏,但是都不美观。如果想自定义美观的标题栏,我们需要自己重写标题栏,话不多说,上视频。以下就是我们自己定义的标题栏,现在我来教一下如何实现这个界面。  这个标题栏可以用在自己的企业开发中。

testBaseTitleBarDemo

以下是源代码文件,我来给大家介绍一下每一个文件的作用:

CFrameLessWidgetBase 该类为基类

CFrameLessWidgetBase .h

/*
无边框窗口基类
可拉伸
其它窗口派生于该类即可
*/

#pragma once
#include <QWidget>

class CFrameLessWidgetBase : public QWidget
{
public:
	CFrameLessWidgetBase(QWidget* parent = nullptr);
	~CFrameLessWidgetBase();

protected:
	bool nativeEvent(const QByteArray& eventType, void* message, long* result) override;

private:
	int mouse_margin = 5;
};

CFrameLessWidgetBase .cpp

#include "CFrameLessWidgetBase.h"

#ifdef Q_OS_WIN
#include <qt_windows.h>
#include <Windows.h>
#include <windowsx.h>
#endif

#pragma comment(lib, "user32.lib")
#pragma comment(lib,"dwmapi.lib")

CFrameLessWidgetBase::CFrameLessWidgetBase(QWidget* parent)
{
	setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);
	setAttribute(Qt::WA_Hover);
}

CFrameLessWidgetBase::~CFrameLessWidgetBase()
{
}

bool CFrameLessWidgetBase::nativeEvent(const QByteArray& eventType, void* message, long* result)
{
	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();

		// 如果鼠标位于内部子控件上,则不进行处理
		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);
}

CTitleBar 重点类(参考以下源码)

CTitleBar .h

#pragma once

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

class CTitleBar : public QWidget
{
	Q_OBJECT

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

protected:
	void paintEvent(QPaintEvent* event) override;
	void mousePressEvent(QMouseEvent* event) override;
	void mouseDoubleClickEvent(QMouseEvent* event) override;
	
signals:
	void sig_close();
	void sig_max(bool isMax);

private slots:
	void on_Clicked();

private:
	Ui::CTitleBar ui;
};

CTitleBar.cpp

#include "CTitleBar.h"
#include <QMouseEvent>
#include <QStyleOption>
#include <QPushButton>
#include <QPainter>

#include <qt_windows.h>
#pragma comment(lib, "user32.lib")

CTitleBar::CTitleBar(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);

	//禁止父窗口影响子窗口样式
	setAttribute(Qt::WA_StyledBackground);

	//左上,右上圆角10;左下,右下圆角0
	this->setStyleSheet("QWidget{background-color:rgb(54,54,54); \
        border-top-left-radius:10px; \
        border-top-right-radius:10px; \
		border-bottom-left-radius:0px; \
		border-bottom-right-radius:0px;}");

	ui.label_title->setText(u8"我是标题");
	ui.label_title->setStyleSheet("QLabel{font-family: Microsoft YaHei; \
		font-size:18px; \
		color:#BDC8E2;background-color:rgb(54,54,54);}");

	ui.btnSet->setFixedSize(32, 32);
	ui.btnSet->setText("");
	ui.btnSet->setFlat(true);
	ui.btnSet->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/set.svg);border:none}" \
		"QPushButton:hover{" \
		"background-color:rgb(99, 99, 99);" \
		"background-image:url(:/titlebar/resource/titlebar/set_hover.svg);border:none;}");

	ui.btnMin->setFixedSize(32, 32);
	ui.btnMin->setText("");
	ui.btnMin->setFlat(true);
	ui.btnMin->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/min.svg);border:none}" \
		"QPushButton:hover{" \
		"background-color:rgb(99, 99, 99);" \
		"background-image:url(:/titlebar/resource/titlebar/min_hover.svg);border:none;}");

	ui.btnMax->setFixedSize(35, 35);
	ui.btnMax->setFlat(true);
	ui.btnMax->setText("");
	ui.btnMax->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/normal.svg);border:none; \
		background-position:center; \
		background-repeat:no-repeat;}  \
		QPushButton:hover{ \
		background-color:rgb(99, 99, 99); \
		background-image:url(:/titlebar/resource/titlebar/normal_hover.svg);border:none;}");

	ui.btnClose->setFixedSize(32, 32);
	ui.btnClose->setFlat(true);
	ui.btnClose->setText("");
	ui.btnClose->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/close.svg);border:none}" \
		"QPushButton:hover{" \
		"background-color:rgb(99, 99, 99);" \
		"background-image:url(:/titlebar/resource/titlebar/close_hover.svg);border:none;}");

	connect(ui.btnMin, SIGNAL(clicked(bool)), this, SLOT(on_Clicked()));
	connect(ui.btnMax, SIGNAL(clicked(bool)), this, SLOT(on_Clicked()));
	connect(ui.btnClose, SIGNAL(clicked(bool)), this, SLOT(on_Clicked()));
}

CTitleBar::~CTitleBar()
{
}

void CTitleBar::paintEvent(QPaintEvent* event)
{
	//决定样式表是否起作用
	/*QStyleOption opt;
	opt.init(this);
	QPainter p(this);
	style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
	QWidget::paintEvent(event);*/
}

void CTitleBar::on_Clicked()
{
	QPushButton* pButton = qobject_cast<QPushButton*>(sender());

	QWidget* pWindow = this->window();

	if (pWindow->isTopLevel())
	{
		if (pButton == ui.btnMin)
		{
			pWindow->showMinimized();
		}
		else if (pButton == ui.btnMax)
		{
			if (pWindow->isMaximized())
			{
				pWindow->showNormal();
				ui.btnMax->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/normal.svg);border:none;  \
					background-position:center; \
					background-repeat:no-repeat;}  \
					QPushButton:hover{ \
					background-color:rgb(99, 99, 99); \
					background-image:url(:/titlebar/resource/titlebar/normal_hover.svg);border:none;}");
			
				emit sig_max(false);
			}
			else
			{
				pWindow->showMaximized();
				ui.btnMax->setStyleSheet("QPushButton{background-image:url(:/titlebar/resource/titlebar/max.svg);border:none;  \
					background-position:center; \
					background-repeat:no-repeat;}  \
					QPushButton:hover{ \
					background-color:rgb(99, 99, 99);  \
					background-image:url(:/titlebar/resource/titlebar/max_hover.svg);border:none;}");

				emit sig_max(true);
			}
		}
		else if (pButton == ui.btnClose)
		{
			emit sig_close();
		}
	}
}

void CTitleBar::mousePressEvent(QMouseEvent* event)
{
	if (ReleaseCapture())
	{
		QWidget* pWindow = this->window();
		if (pWindow->isTopLevel())
		{
			SendMessage(HWND(pWindow->winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
		}
	}

	event->ignore();
}

void CTitleBar::mouseDoubleClickEvent(QMouseEvent* event)
{
	emit ui.btnMax->clicked();
}

CTopWidget.h

#pragma once
#include <QWidget>

class CTopWidget : public QWidget
{
	Q_OBJECT

public:
	CTopWidget(QWidget* p = nullptr);
	~CTopWidget();

signals:
	void sig_close();
	void sig_max(bool isMax);
};

CTopWidget.cpp

#include "CTopWidget.h"
#include "CTitleBar.h"
#include <QVBoxLayout>

CTopWidget::CTopWidget(QWidget* p)
    :QWidget(p)
{
    setAttribute(Qt::WA_StyledBackground);
    this->setStyleSheet("QWidget{background-color:rgb(255, 254, 253);border-radius:10px;}");
    /*this->setStyleSheet("QWidget{background-color:rgb(255, 254, 253); \
        border-bottom-left-radius:15px; \
        border-bottom-right-radius:15px;}");*/

    QVBoxLayout* pVLay = new QVBoxLayout(this);
    CTitleBar* pTitle = new CTitleBar(this);
    QWidget* pWidget = new QWidget(this);

    //需要指定pWidget最小尺寸,或最大尺寸
    pWidget->setMinimumSize(1200, 800);

    pVLay->addWidget(pTitle);
    pVLay->addWidget(pWidget);
    pVLay->setContentsMargins(0, 0, 0, 0);
    setLayout(pVLay);

    connect(pTitle, &CTitleBar::sig_close, this, &CTopWidget::sig_close);
    connect(pTitle, &CTitleBar::sig_max, this, &CTopWidget::sig_max);
}

CTopWidget::~CTopWidget()
{
}

MainWidget.h

#pragma once

#include <QtWidgets/QWidget>
#include "CFrameLessWidgetBase.h"
#include "CTopWidget.h"
#include <QVBoxLayout>
#include <QGraphicsDropShadowEffect>

//派生于CFrameLessWidgetBase
class MainWidget : public CFrameLessWidgetBase
{
    Q_OBJECT

public:
    MainWidget(QWidget *parent = Q_NULLPTR);

private slots:
    void onClose();
    void onDoMax(bool isMax);

private:
    QVBoxLayout* m_pMainVLay = nullptr;
    CTopWidget* m_pTopWidget = nullptr;
    QGraphicsDropShadowEffect* m_pShadow = nullptr;
};

MainWidget.cpp

#include "MainWidget.h"



MainWidget::MainWidget(QWidget *parent)
    : CFrameLessWidgetBase(parent)
{
    //设置窗体透明
    this->setAttribute(Qt::WA_TranslucentBackground, true);

    m_pMainVLay = new QVBoxLayout(this);
    m_pTopWidget = new CTopWidget(this);
    m_pMainVLay->addWidget(m_pTopWidget);
	
    int shadow_width = 30;
    m_pMainVLay->setContentsMargins(shadow_width, shadow_width, shadow_width, shadow_width);
    setLayout(m_pMainVLay);

    //给顶层widget设置背景颜色,不然看不见,因为底层widget已经透明了
    m_pTopWidget->setStyleSheet("background-color:rgb(255, 254, 253)");
    m_pShadow = new QGraphicsDropShadowEffect(this);

    //设置阴影距离
    m_pShadow->setOffset(0, 0);

    //设置阴影颜色  686868
    m_pShadow->setColor(QColor("#0000FF"));

    //设置阴影区域
    m_pShadow->setBlurRadius(shadow_width - 5);

    //给顶层QWidget设置阴影
    m_pTopWidget->setGraphicsEffect(m_pShadow);

    connect(m_pTopWidget, &CTopWidget::sig_close, this, &MainWidget::onClose);
    connect(m_pTopWidget, &CTopWidget::sig_max, this, &MainWidget::onDoMax);
}

void MainWidget::onClose()
{
    close();
}

void MainWidget::onDoMax(bool isMax)
{
    int shadow_width = 25;
    if (isMax)
    {
        shadow_width = 0;
    }
    else
    {

    }

    m_pMainVLay->setContentsMargins(shadow_width, shadow_width, shadow_width, shadow_width);

    //设置阴影区域
    m_pShadow->setBlurRadius(shadow_width);

    //给顶层QWidget设置阴影
    m_pTopWidget->setGraphicsEffect(m_pShadow);
}

最后,main.cpp

#include "MainWidget.h"
#include <QtWidgets/QApplication>

//#pragma comment(lib, "User32.lib")

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWidget w;
    w.show();
    return a.exec();
}
;