1.简介
本次介绍自定义窗口标题头实现方法,跟之前的《QT C++ QWidget 实现毛玻璃窗口,透明+模糊效果》有关联,有兴趣的可以去看看。
传送门:QT C++ QWidget 实现毛玻璃窗口,透明+模糊效果
2.效果
2.1传统窗口标题头
2.1 自定义标题头
3.目录结构
4.步骤
1. 创建新项目 QWidget
2.创建CglasseFfect 类,实现毛玻璃效果+去掉传统窗口标题头。CglasseFfect类又代码,复制使用就行。
3.创建属于自己的自定义标题头 例如我的 "TitleBar“
4.调用自定义标题头
QVBoxLayout* vboxLayout = new QVBoxLayout(this);
titleBar = new TitleBar(this);
vboxLayout->addWidget(titleBar,1);
vboxLayout->addWidget(ui.widget,10);
vboxLayout->setSpacing(0);
vboxLayout->setContentsMargins(0, 0, 0, 0);
titleBar->setMouseTracking(true);
ui.widget->setMouseTracking(true);
5.代码实现
CglasseFfect类
.h文件
#pragma once
#include <QWidget>
#include <QPaintEvent>
#include <QtWin>
#include <QPainter>
// 定义当前鼠标所处状态;
enum WindowStretchRectState
{
NO_SELECT = 0, // 鼠标未进入下方矩形区域;
LEFT_TOP_RECT, // 鼠标在左上角区域;
TOP_BORDER, // 鼠标在上边框区域;
RIGHT_TOP_RECT, // 鼠标在右上角区域;
RIGHT_BORDER, // 鼠标在右边框区域;
RIGHT_BOTTOM_RECT, // 鼠标在右下角区域;
BOTTOM_BORDER, // 鼠标在下边框区域;
LEFT_BOTTOM_RECT, // 鼠标在左下角区域;
LEFT_BORDER // 鼠标在左边框区域;
};
class CglasseFfect : public QWidget
{
Q_OBJECT
public:
CglasseFfect(QWidget*parent);
~CglasseFfect();
void setGlassBackgroundR(int R);
void setGlassBackgroundG(int G);
void setGlassBackgroundB(int B);
void setGlassBackgroundA(int A);
void setGlassBackgroundO(double O);
void paintEvent(QPaintEvent* ev);
private:
QColor*bgColor = nullptr;
QRect m_leftTopRect;
QRect m_leftBottomRect;
QRect m_rightTopRect;
QRect m_rightBottomRect;
QRect m_topBorderRect;
QRect m_rightBorderRect;
QRect m_bottomBorderRect;
QRect m_leftBorderRect;
WindowStretchRectState m_stretchRectState;
bool m_isMousePressed;
bool m_isWindowMax;
QPoint m_endPoint;
QPoint m_startPoint;
QRect m_windowRectBeforeStretch;
int m_windowMinWidth;
int m_windowMinHeight;
WindowStretchRectState getCurrentStretchState(QPoint cursorPos);
void updateMouseStyle(WindowStretchRectState stretchState);
void calculateCurrentStrechRect();
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void updateWindowSize();
};
typedef enum _WINDOWCOMPOSITIONATTRIB
{
CA_UNDEFINED = 0,
WCA_NCRENDERING_ENABLED = 1,
WCA_NCRENDERING_POLICY = 2,
WCA_TRANSITIONS_FORCEDISABLED = 3,
WCA_ALLOW_NCPAINT = 4,
WCA_CAPTION_BUTTON_BOUNDS = 5,
WCA_NONCLIENT_RTL_LAYOUT = 6,
WCA_FORCE_ICONIC_REPRESENTATION = 7,
WCA_EXTENDED_FRAME_BOUNDS = 8,
WCA_HAS_ICONIC_BITMAP = 9,
WCA_THEME_ATTRIBUTES = 10,
WCA_NCRENDERING_EXILED = 11,
WCA_NCADORNMENTINFO = 12,
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
WCA_VIDEO_OVERLAY_ACTIVE = 14,
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
WCA_DISALLOW_PEEK = 16,
WCA_CLOAK = 17,
WCA_CLOAKED = 18,
WCA_ACCENT_POLICY = 19,//
WCA_FREEZE_REPRESENTATION = 20,
WCA_EVER_UNCLOAKED = 21,
WCA_VISUAL_OWNER = 22,
WCA_LAST = 23
} WINDOWCOMPOSITIONATTRIB;
typedef struct _WINDOWCOMPOSITIONATTRIBDATA
{
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
} WINDOWCOMPOSITIONATTRIBDATA;
typedef enum _ACCENT_STATE
{
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_INVALID_STATE = 4
} ACCENT_STATE;
typedef struct _ACCENT_POLICY
{
ACCENT_STATE AccentState;
DWORD AccentFlags;
DWORD GradientColor;
DWORD AnimationId;
} ACCENT_POLICY;
WINUSERAPI
BOOL
WINAPI
GetWindowCompositionAttribute(
_In_ HWND hWnd,
_Inout_ WINDOWCOMPOSITIONATTRIBDATA* pAttrData);
typedef BOOL(WINAPI* pfnGetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
WINUSERAPI
BOOL
WINAPI
SetWindowCompositionAttribute(
_In_ HWND hWnd,
_Inout_ WINDOWCOMPOSITIONATTRIBDATA* pAttrData);
typedef BOOL(WINAPI* pfnSetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
.cpp文件
#include "CglasseFfect.h"
#include <QDebug>
#define STRETCH_RECT_HEIGHT 20 // 拉伸小矩形的高度;
#define STRETCH_RECT_WIDTH 20
CglasseFfect::CglasseFfect(QWidget* parent)
: QWidget(parent)
{
setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint);
//setWindowFlags( Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);//背景半透明属性设置 //窗口透明
HWND hWnd = HWND(this->winId());
setWindowOpacity(1);
//SetLayeredWindowAttributes(
// hWnd, // 窗口句柄
// 2, // 颜色键
// 124, // 透明度,取值为0-255
// LWA_ALPHA //
//);
HMODULE hUser = GetModuleHandle(L"user32.dll");
if (hUser)
{
pfnSetWindowCompositionAttribute setWindowCompositionAttribute = (pfnSetWindowCompositionAttribute)GetProcAddress(hUser, "SetWindowCompositionAttribute");
if (setWindowCompositionAttribute)
{
ACCENT_POLICY accent = { ACCENT_ENABLE_BLURBEHIND, 0, 0, 0 };
WINDOWCOMPOSITIONATTRIBDATA data;
data.Attrib = WCA_ACCENT_POLICY;
data.pvData = &accent;
data.cbData = sizeof(accent);
setWindowCompositionAttribute(hWnd, &data);
}
}
bgColor = new QColor(0, 0, 0,1);
setMouseTracking(true);
m_stretchRectState = NO_SELECT;
m_windowMinHeight = size().height();
m_windowMinWidth = size().width();
m_isWindowMax = false;
m_isMousePressed = false;
}
CglasseFfect::~CglasseFfect()
{}
void CglasseFfect::setGlassBackgroundR(int R)
{
bgColor->setRed(R);
}
void CglasseFfect::setGlassBackgroundG(int G)
{
bgColor->setGreen(G);
}
void CglasseFfect::setGlassBackgroundB(int B)
{
bgColor->setBlue(B);
}
void CglasseFfect::setGlassBackgroundA(int A)
{
bgColor->setAlpha(A);
}
void CglasseFfect::setGlassBackgroundO(double O)
{
setWindowOpacity(O);
}
void CglasseFfect::paintEvent(QPaintEvent * ev)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
painter.setBrush(*bgColor);
//painter.drawRoundedRect(rect(), 20, 20);
painter.drawRect(0, 0, width(), height());
}
void CglasseFfect::calculateCurrentStrechRect()
{
// 四个角Rect;
m_leftTopRect = QRect(0, 0, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT);
m_rightTopRect = QRect(this->width() - STRETCH_RECT_WIDTH, 0, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT);
m_rightBottomRect = QRect(this->width() + 7 - STRETCH_RECT_WIDTH, this->height() + 7 - STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT);
// 四条边Rect;
m_topBorderRect = QRect(STRETCH_RECT_WIDTH, 0, this->width() - STRETCH_RECT_WIDTH * 2, STRETCH_RECT_HEIGHT );
m_rightBorderRect = QRect(this->width() + 14 - STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, this->height() - STRETCH_RECT_HEIGHT * 2);
m_bottomBorderRect = QRect(STRETCH_RECT_WIDTH, this->height() + 14 - STRETCH_RECT_HEIGHT, this->width() - STRETCH_RECT_WIDTH * 2, STRETCH_RECT_HEIGHT);
m_leftBorderRect = QRect(0, STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, this->height() - STRETCH_RECT_HEIGHT * 2);
}
// 根据当前鼠标位置,定位鼠标在具体哪一块拉伸区域;
WindowStretchRectState CglasseFfect::getCurrentStretchState(QPoint cursorPos)
{
WindowStretchRectState stretchState;
if (m_leftTopRect.contains(cursorPos))
{
stretchState = LEFT_TOP_RECT;
}
else if (m_rightTopRect.contains(cursorPos))
{
stretchState = RIGHT_TOP_RECT;
}
else if (m_rightBottomRect.contains(cursorPos))
{
stretchState = RIGHT_BOTTOM_RECT;
}
else if (m_leftBottomRect.contains(cursorPos))
{
stretchState = LEFT_BOTTOM_RECT;
}
else if (m_topBorderRect.contains(cursorPos))
{
stretchState = TOP_BORDER;
}
else if (m_rightBorderRect.contains(cursorPos))
{
stretchState = RIGHT_BORDER;
}
else if (m_bottomBorderRect.contains(cursorPos))
{
stretchState = BOTTOM_BORDER;
}
else if (m_leftBorderRect.contains(cursorPos))
{
stretchState = LEFT_BORDER;
}
else
{
stretchState = NO_SELECT;
}
return stretchState;
}
// 根据getCurrentStretchState返回状态进行更新鼠标样式;
void CglasseFfect::updateMouseStyle(WindowStretchRectState stretchState)
{
switch (stretchState)
{
case NO_SELECT:
setCursor(Qt::ArrowCursor);
break;
case LEFT_TOP_RECT:
case RIGHT_BOTTOM_RECT:
setCursor(Qt::SizeFDiagCursor);
break;
case TOP_BORDER:
case BOTTOM_BORDER:
setCursor(Qt::SizeVerCursor);
break;
case RIGHT_TOP_RECT:
case LEFT_BOTTOM_RECT:
setCursor(Qt::SizeBDiagCursor);
break;
case LEFT_BORDER:
case RIGHT_BORDER:
setCursor(Qt::SizeHorCursor);
break;
default:
setCursor(Qt::ArrowCursor);
break;
}
}
void CglasseFfect::mouseMoveEvent(QMouseEvent* event)
{
// 如果窗口最大化是不能拉伸的;
// 也不用更新鼠标样式;
if (m_isWindowMax)
{
return __super::mouseMoveEvent(event);
}
// 如果当前鼠标未按下,则根据当前鼠标的位置更新鼠标的状态及样式;
if (!m_isMousePressed)
{
QPoint cursorPos = event->pos();
// 根据当前鼠标的位置显示不同的样式;
m_stretchRectState = getCurrentStretchState(cursorPos);
updateMouseStyle(m_stretchRectState);
}
// 如果当前鼠标左键已经按下,则记录下第二个点的位置,并更新窗口的大小;
else
{
m_endPoint = this->mapToGlobal(event->pos());
updateWindowSize();
}
return __super::mouseMoveEvent(event);
}
void CglasseFfect::mousePressEvent(QMouseEvent* event)
{
// 当前鼠标进入了以上指定的8个区域,并且是左键按下时才开始进行窗口拉伸;
if (m_stretchRectState != NO_SELECT && event->button() == Qt::LeftButton)
{
m_isMousePressed = true;
// 记录下当前鼠标位置,为后面计算拉伸位置;
m_startPoint = this->mapToGlobal(event->pos());
// 保存下拉伸前的窗口位置及大小;
m_windowRectBeforeStretch = this->geometry();
}
return __super::mousePressEvent(event);
}
void CglasseFfect::mouseReleaseEvent(QMouseEvent* event)
{
// 鼠标松开后意味之窗口拉伸结束,置标志位,并且重新计算用于拉伸的8个区域Rect;
m_isMousePressed = false;
calculateCurrentStrechRect();
return __super::mouseReleaseEvent(event);
}
// 拉伸窗口过程中,根据记录的坐标更新窗口大小;
// 拉伸窗口过程中,根据记录的坐标更新窗口大小;
void CglasseFfect::updateWindowSize()
{
// 拉伸时要注意设置窗口最小值;
QRect windowRect = m_windowRectBeforeStretch;
int delValue_X = m_startPoint.x() - m_endPoint.x();
int delValue_Y = m_startPoint.y() - m_endPoint.y();
if (m_stretchRectState == LEFT_BORDER)
{
QPoint topLeftPoint = windowRect.topLeft();
topLeftPoint.setX(topLeftPoint.x() - delValue_X);
windowRect.setTopLeft(topLeftPoint);
}
else if (m_stretchRectState == RIGHT_BORDER)
{
QPoint bottomRightPoint = windowRect.bottomRight();
bottomRightPoint.setX(bottomRightPoint.x() - delValue_X);
windowRect.setBottomRight(bottomRightPoint);
}
else if (m_stretchRectState == TOP_BORDER)
{
QPoint topLeftPoint = windowRect.topLeft();
topLeftPoint.setY(topLeftPoint.y() - delValue_Y);
windowRect.setTopLeft(topLeftPoint);
}
else if (m_stretchRectState == BOTTOM_BORDER)
{
QPoint bottomRightPoint = windowRect.bottomRight();
bottomRightPoint.setY(bottomRightPoint.y() - delValue_Y);
windowRect.setBottomRight(bottomRightPoint);
}
else if (m_stretchRectState == LEFT_TOP_RECT)
{
QPoint topLeftPoint = windowRect.topLeft();
topLeftPoint.setX(topLeftPoint.x() - delValue_X);
topLeftPoint.setY(topLeftPoint.y() - delValue_Y);
windowRect.setTopLeft(topLeftPoint);
}
else if (m_stretchRectState == RIGHT_TOP_RECT)
{
QPoint topRightPoint = windowRect.topRight();
topRightPoint.setX(topRightPoint.x() - delValue_X);
topRightPoint.setY(topRightPoint.y() - delValue_Y);
windowRect.setTopRight(topRightPoint);
}
else if (m_stretchRectState == RIGHT_BOTTOM_RECT)
{
QPoint bottomRightPoint = windowRect.bottomRight();
bottomRightPoint.setX(bottomRightPoint.x() - delValue_X);
bottomRightPoint.setY(bottomRightPoint.y() - delValue_Y);
windowRect.setBottomRight(bottomRightPoint);
}
else if (m_stretchRectState == LEFT_BOTTOM_RECT)
{
QPoint bottomLeftPoint = windowRect.bottomLeft();
bottomLeftPoint.setX(bottomLeftPoint.x() - delValue_X);
bottomLeftPoint.setY(bottomLeftPoint.y() - delValue_Y);
windowRect.setBottomLeft(bottomLeftPoint);
}
// 避免宽或高为零窗口显示有误,这里给窗口设置最小拉伸高度、宽度;
if (windowRect.width() < m_windowMinWidth)
{
windowRect.setLeft(this->geometry().left());
windowRect.setWidth(m_windowMinWidth);
}
if (windowRect.height() < m_windowMinHeight)
{
windowRect.setTop(this->geometry().top());
windowRect.setHeight(m_windowMinHeight);
}
this->setGeometry(windowRect);
}
TitleBar类(自定义窗口标题头)
.h文件
#pragma once
#include <QWidget>
#include "ui_TitleBar.h"
#include <QMouseEvent>
class TitleBar : public QWidget
{
Q_OBJECT
public:
TitleBar(QWidget* parent = nullptr);
~TitleBar();
void setTopText(QString text);
void setTopTextQFont(QFont font); // 字体为Arial,大小为20)
void setTopTextStyleSheet(QString style); // 字体为Arial,大小为20)
void setTopColor(QString style); //设置头部样式
private:
void mousePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
public slots:
void on_btnCloseWindow_clicked();
void on_Btnwindowmax_clicked();
void on_BtnMinimizeWin_clicked();
signals:
void btnCloseWindowSig();
void BtnwindowmaxSig();
void BtnMinimizeWinSig();
private:
Ui::TitleBarClass ui;
QWidget* HomeWIdget = nullptr;
// 移动窗口的变量;
bool m_isPressed;
QPoint m_startMovePos;
bool m_bIsShowUserImage = false;
QLabel* UserNameLabel = nullptr;
};
.cpp文件
#include "TitleBar.h"
#include <QMouseEvent>
#include <QHBoxLayout>
#include<QLabel>
#include <QDebug>
TitleBar::TitleBar(QWidget* parent)
: QWidget(parent)
{
ui.setupUi(this);
HomeWIdget = parent;
UserNameLabel = new QLabel(this);
UserNameLabel->setText(u8"冯诺依曼");
ui.horizontalLayout_2->addWidget(UserNameLabel);
UserNameLabel->setVisible(false);
}
TitleBar::~TitleBar()
{
}
void TitleBar::setTopText(QString text)
{
ui.label->setText(text);
}
void TitleBar::setTopTextQFont(QFont font)
{
ui.label->setFont(font);
}
void TitleBar::setTopColor(QString style)
{
QString mes = "#widget{" + style + " }";
qDebug() << mes;
ui.widget->setStyleSheet(mes);
//this->setStyleSheet("background-color:rgb(255, 255, 255);");
}
void TitleBar::setTopTextStyleSheet(QString style)
{
ui.label->setStyleSheet(style);
}
//---------------------------------
// 以下通过mousePressEvent、mouseMoveEvent、mouseReleaseEvent三个事件实现了鼠标拖动标题栏移动窗口的效果;
void TitleBar::mousePressEvent(QMouseEvent* event)
{
m_isPressed = true;
m_startMovePos = event->globalPos();
return QWidget::mousePressEvent(event);
}
void TitleBar::mouseMoveEvent(QMouseEvent* event)
{
if (m_isPressed)
{
QPoint movePoint = event->globalPos() - m_startMovePos;
QPoint widgetPos = this->parentWidget()->pos();
m_startMovePos = event->globalPos();
this->parentWidget()->move(widgetPos.x() + movePoint.x(), widgetPos.y() + movePoint.y());
}
return QWidget::mouseMoveEvent(event);
}
void TitleBar::mouseReleaseEvent(QMouseEvent* event)
{
m_isPressed = false;
return QWidget::mouseReleaseEvent(event);
}
void TitleBar::on_Btnwindowmax_clicked()
{
if (HomeWIdget->windowState() == Qt::WindowMaximized)
HomeWIdget->setWindowState(Qt::WindowNoState); // 恢复正常状态
else
HomeWIdget->setWindowState(Qt::WindowMaximized); // 最大化
}
void TitleBar::on_BtnMinimizeWin_clicked()
{
HomeWIdget->showMinimized();
}
void TitleBar::on_btnCloseWindow_clicked()
{
HomeWIdget->close();
}
.ui文件
自定义头调用
.h文件
#pragma once
#include <QtWidgets/QWidget>
#include "ui_glassui.h"
#include "CglasseFfect.h"
#include "TitleBar.h"
class glassui : public CglasseFfect
{
Q_OBJECT
public:
glassui(QWidget *parent = nullptr);
~glassui();
void setValueR(int R);
void setValueG(int G);
void setValueB(int B);
void setValueA(int A);
void setValueO(int O);
public slots:
private:
Ui::glassuiClass ui;
int cruuR = 0;
int G = 0;
int B = 0;
int A = 0;
TitleBar* titleBar = nullptr;
};
.cpp文件
#include "glassui.h"
#include <QDebug>
glassui::glassui(QWidget *parent)
: CglasseFfect(parent)
{
ui.setupUi(this);
QObject::connect(ui.horizontalSliderB, &QSlider::valueChanged, this, &glassui::setValueB);
QObject::connect(ui.horizontalSliderR, &QSlider::valueChanged, this, &glassui::setValueR);
QObject::connect(ui.horizontalSliderG, &QSlider::valueChanged, this, &glassui::setValueG);
QObject::connect(ui.horizontalSliderA, &QSlider::valueChanged, this, &glassui::setValueA);
QObject::connect(ui.horizontalSliderO, &QSlider::valueChanged, this, &glassui::setValueO);
QVBoxLayout* vboxLayout = new QVBoxLayout(this);
titleBar = new TitleBar(this);
vboxLayout->addWidget(titleBar,1);
vboxLayout->addWidget(ui.widget,10);
vboxLayout->setSpacing(0);
vboxLayout->setContentsMargins(0, 0, 0, 0);
titleBar->setMouseTracking(true);
ui.widget->setMouseTracking(true);
// setContentsMargins
}
glassui::~glassui()
{}
void glassui::setValueG(int G)
{
setGlassBackgroundR(G);
ui.spinBoxG->setValue(G);
qDebug() << "G" << G;
update();
}
void glassui::setValueB(int B)
{
setGlassBackgroundB(B);
ui.spinBoxB->setValue(B);
qDebug() << "B" << B;
update();
}
void glassui::setValueA(int A)
{
setGlassBackgroundA(A);
qDebug() << "A" << A;
ui.spinBoxA->setValue(A);
update();
}
void glassui::setValueO(int O)
{
setGlassBackgroundO(O*0.01);
qDebug() << "O" << O;
ui.spinBoxO->setValue(O * 0.01);
update();
}
void glassui::setValueR(int R)
{
setGlassBackgroundR(R);
qDebug() << "G" << G;
ui.spinBoxR->setValue(R);
update();
}
6.视频效果
qt C++ 自定义标题头视频效果