<>前言


  我们经常需要自定义标题栏,那么去掉标题栏是非常有必要。但是去掉标题栏之后边框阴影也会消失,感觉光秃秃的,不太舒服。接下来我们将讨论添加边框阴影的几种解决方案。


<>解决方案

* 如果是Windows平台,那么可以调用Windows相关API。
* 使用Qt的QGraphicsDropShadowEffect类来实现。
* 使用Qt的qDrawBorderPixmap函数来实现。
* 自己构造出边框阴影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的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
<https://download.csdn.net/download/a844651990/10841366>
GitHub: https://github.com/FlyWM/ShadowWidget
<https://github.com/FlyWM/ShadowWidget>

友情链接
ioDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信