Qt去掉标题栏之后添加边框阴影的解决方案
本文来源链接:https://blog.csdn.net/a844651990/article/details/84943693
前言
我们经常需要自定义标题栏,那么去掉标题栏是非常有必要。但是去掉标题栏之后边框阴影也会消失,感觉光秃秃的,不太舒服。接下来我们将讨论添加边框阴影的几种解决方案。
解决方案
1、如果是Windows平台,那么可以调用Windows相关API。
2、使用Qt的QGraphicsDropShadowEffect类来实现。
3、使用Qt的qDrawBorderPixmap函数来实现。
4、自己构造出边框阴影QImage并绘制(参考为知笔记源码)。
源码实现
一、调用Windows相关API
我们调用的是dwmapi.dll即Microsoft Desktop Window Manager API(桌面窗口管理器DWM 的公用界面)的动态链接库的相关函数。
#include "WinAPIShadowWidget.h" #include "windwmapi.h" WinAPIShadowWidget::WinAPIShadowWidget(QWidget *parent) : QWidget(parent) { setWindowFlags(windowFlags() | Qt::FramelessWindowHint); HWND hwnd = (HWND)this->winId(); DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); // 此行代码可以带回Aero效果,同时也带回了标题栏和边框,在nativeEvent()会再次去掉标题栏 ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION); //we better left 1 piexl width of border untouch, so OS can draw nice shadow around it const MARGINS shadow = { 1, 1, 1, 1 }; WinDwmapi::instance()->DwmExtendFrameIntoClientArea(HWND(winId()), &shadow); } bool WinAPIShadowWidget::nativeEvent(const QByteArray &eventType, void *message, long *result) { MSG* msg = (MSG *)message; switch (msg->message) { case WM_NCCALCSIZE: { // this kills the window frame and title bar we added with WS_THICKFRAME and WS_CAPTION *result = 0; return true; } default: return QWidget::nativeEvent(eventType, message, result); } } ... HRESULT WinDwmapi::DwmExtendFrameIntoClientArea(HWND hWnd, const MARGINS *pMarInset) const { if (dwm_extendframe_into_client_area_) { return dwm_extendframe_into_client_area_(hWnd, pMarInset); } return E_NOTIMPL; } ... if (dwmapi_dll_) { dwm_is_composition_enabled_ = \ reinterpret_cast<DwmIsCompositionEnabledPtr>(GetProcAddress(dwmapi_dll_, "DwmIsCompositionEnabled")); dwm_extendframe_into_client_area_ = \ reinterpret_cast<DwmExtendFrameIntoClientAreaPtr>(GetProcAddress(dwmapi_dll_, "DwmExtendFrameIntoClientArea")); }
效果图:
二、使用Qt的QGraphicsDropShadowEffect类来实现
QGraphicsDropShadowEffect类提供了一个投影效果。可以使用setColor()函数修改投影的颜色。可以使用setOffset()函数修改阴影偏移量,使用setBlurRadius()函数修改阴影的半径。默认情况下,投影是半透明的深灰色(QColor(63, 63, 63, 180)阴影,模糊半径为1,向右下角偏移8个像素。将一个QWidget嵌入到另一个QWidget中,被嵌入的QWidget背景透明。
ShadowEffectWidget::ShadowEffectWidget(QWidget *parent) : QWidget(parent) { resize(400, 300); setWindowFlags(windowFlags() | Qt::FramelessWindowHint); setAttribute(Qt::WA_TranslucentBackground); QWidget *pCentralWidget = new QWidget(this); pCentralWidget->setStyleSheet("background-color: white"); QHBoxLayout *pLayout = new QHBoxLayout(this); pLayout->addWidget(pCentralWidget); pLayout->setContentsMargins(20, 20, 20, 20); QGraphicsDropShadowEffect *pEffect = new QGraphicsDropShadowEffect(pCentralWidget); pEffect->setOffset(0, 0); pEffect->setColor(QColor(QStringLiteral("black"))); pEffect->setBlurRadius(30); pCentralWidget->setGraphicsEffect(pEffect); }
效果图:
还可参考:Qt之使用QGraphicsDropShadowEffect添加窗口边框以及文字阴影效果
三、使用Qt的qDrawBorderPixmap函数来实现
qDrawBorderPixmap函数用于将一个像素图绘制到一个矩形的边缘。使用给定的绘图器将给定的像素映射绘制到给定的目标矩形中。pixmap将被分割成九个部分,并根据边缘结构绘制。需要我们提前做好边框阴影的图片。但是据说这种方式效率并不高,有待考证。
DrawBorderPixmapWidget::DrawBorderPixmapWidget(QWidget *parent) : QWidget(parent) { resize(800, 600); setWindowFlags(Qt::FramelessWindowHint); setAttribute(Qt::WA_TranslucentBackground); } void DrawBorderPixmapWidget::paintEvent(QPaintEvent *e) { Q_UNUSED(e) QPainter painter(this); QPixmap pixmap(":/client-shadow.png"); qDrawBorderPixmap(&painter, this->rect(), QMargins(8, 8, 8, 8), pixmap); // 绘制中心区域的背景色(不然会是透明的) QRect rect(this->rect().x()+8, this->rect().y()+8, this->rect().width()-16, this->rect().height()-16); painter.fillRect(rect, QColor(255, 255, 255)); }
效果图:
四、自己构造出边框阴影QImage并绘制
这种方式稍微麻烦点,但是比较灵活,效率也很可观,推荐使用。代码有点多,先上主要的。
inline unsigned char MakeAlpha(int i, double f, int nSize) { if (i == nSize) f *= 1.2; // double f2 = 1 - cos((double)i / nSize * 3.14 / 2); // return int(fabs((i * f) * f2)); } QImage MakeShadowImage(int shadowSize, bool activated) { int size = shadowSize * 2 + 10; QImage image(size, size, QImage::Format_ARGB32); image.fill(QColor(Qt::black)); // double f = activated ? 4.0 : 1.0; // QSize szImage = image.size(); // //left for (int y = shadowSize; y < szImage.height() - shadowSize; y++) { for (int x = 0; x < shadowSize; x++) { int i = x + 1; int alpha = MakeAlpha(i, f, shadowSize); image.setPixelColor(x, y, QColor(0, 0, 0, alpha)); } } //right for (int y = shadowSize; y < szImage.height() - shadowSize; y++) { for (int x = szImage.width() - shadowSize - 1; x < szImage.width(); x++) { int i = szImage.width() - x; int alpha = MakeAlpha(i, f, shadowSize); image.setPixelColor(x, y, QColor(0, 0, 0, alpha)); } } //top for (int y = 0; y < shadowSize; y++) { int i = y + 1; for (int x = shadowSize; x < szImage.width() - shadowSize; x++) { int alpha = MakeAlpha(i, f, shadowSize); image.setPixelColor(x, y, QColor(0, 0, 0, alpha)); } // } //bottom for (int y = szImage.height() - shadowSize - 1; y < szImage.height(); y++) { int i = szImage.height() - y; for (int x = shadowSize; x < szImage.width() - shadowSize; x++) { int alpha = MakeAlpha(i, f, shadowSize); image.setPixelColor(x, y, QColor(0, 0, 0, alpha)); } } // int parentRoundSize = 3; // for (int x = 0; x < shadowSize + parentRoundSize; x++) { for (int y = 0; y < shadowSize + parentRoundSize; y++) { int xx = (shadowSize + parentRoundSize) - x; int yy = (shadowSize + parentRoundSize) - y; int i = int(sqrt(double(xx * xx + yy * yy))); i = std::min<int>(shadowSize + parentRoundSize, i); i -= parentRoundSize; i = shadowSize - i; // int alpha = MakeAlpha(i, f, shadowSize); image.setPixelColor(x, y, QColor(0, 0, 0, alpha)); } } // for (int x = szImage.width() - shadowSize - parentRoundSize; x < szImage.width(); x++) { for (int y = 0; y < shadowSize + parentRoundSize; y++) { int xx = (shadowSize + parentRoundSize) - (szImage.width() - x); int yy = (shadowSize + parentRoundSize) - y; int i = int(sqrt(double(xx * xx + yy * yy))); i = std::min<int>(shadowSize + parentRoundSize, i); i -= parentRoundSize; i = shadowSize - i; // int alpha = MakeAlpha(i, f, shadowSize); image.setPixelColor(x, y, QColor(0, 0, 0, alpha)); } } // for (int x = 0; x < shadowSize + parentRoundSize; x++) { for (int y = szImage.height() - shadowSize - parentRoundSize; y < szImage.height(); y++) { int xx = (shadowSize + parentRoundSize) - x; int yy = (shadowSize + parentRoundSize) - (szImage.height() - y); int i = int(sqrt(double(xx * xx + yy * yy))); i = std::min<int>(shadowSize + parentRoundSize, i); i -= parentRoundSize; i = shadowSize - i; // int alpha = MakeAlpha(i, f, shadowSize); image.setPixelColor(x, y, QColor(0, 0, 0, alpha)); } } // for (int x = szImage.width() - shadowSize - parentRoundSize; x < szImage.width(); x++) { for (int y = szImage.height() - shadowSize - parentRoundSize; y < szImage.height(); y++) { int xx = (shadowSize + parentRoundSize) - (szImage.width() - x); int yy = (shadowSize + parentRoundSize) - (szImage.height() - y); int i = int(sqrt(double(xx * xx + yy * yy))); i = std::min<int>(shadowSize + parentRoundSize, i); i -= parentRoundSize; i = shadowSize - i; // int alpha = MakeAlpha(i, f, shadowSize); image.setPixelColor(x, y, QColor(0, 0, 0, alpha)); } } // int borderR = 165; int borderG = 165; int borderB = 165; // if (activated) { borderR = 68; borderG = 138; borderB = 255; // borderR = 0; // borderG = 0; // borderB = 0; } // int borderSize = 1; //left for (int i = 0; i < borderSize; i++) { for (int y = shadowSize - 1; y < szImage.height() - shadowSize + 1; y++) { int x = shadowSize - i - 1; image.setPixelColor(x, y, QColor(borderR, borderG, borderB, 255)); } } //right for (int i = 0; i < borderSize; i++) { for (int y = shadowSize - 1; y < szImage.height() - shadowSize + 1; y++) { int x = szImage.width() - shadowSize - 1 + i + 1; image.setPixelColor(x, y, QColor(borderR, borderG, borderB, 255)); } } //top for (int i = 0; i < borderSize; i++) { for (int x = shadowSize; x < szImage.width() - shadowSize; x++) { int y = shadowSize - i - 1; image.setPixelColor(x, y, QColor(borderR, borderG, borderB, 255)); } } //bottom for (int i = 0; i < borderSize; i++) { for (int x = shadowSize; x < szImage.width() - shadowSize; x++) { int y = szImage.height() - shadowSize - 1 + i + 1; image.setPixelColor(x, y, QColor(borderR, borderG, borderB, 255)); } } // return image; }
调节阴影大小时,只需要调节shadowSize的大小即可。
ShadowWidget::ShadowWidget(int shadowSize, QWidget *parent) : m_shadowSize(shadowSize) , QWidget(parent) , m_shadow(new Skin9GridImage()) { setAttribute(Qt::WA_TranslucentBackground); setWindowFlag(Qt::FramelessWindowHint); setMouseTracking(true); // QImage image = MakeShadowImage(shadowSize, true); m_shadow->setImage(image, QPoint(shadowSize + 1, shadowSize + 1)); } void ShadowWidget::paintEvent(QPaintEvent *e) { Q_UNUSED(e) QPainter painter(this); m_shadow->drawBorder(&painter, rect()); }
效果图:
源码下载
CSDN: https://download.csdn.net/download/a844651990/10841366
GitHub: https://github.com/FlyWM/ShadowWidget