设置Qt::FramelessWindowHint后控件外部阴影消失的技术咨询
我之前也碰到过一模一样的坑!当设置Qt::FramelessWindowHint和Qt::WA_TranslucentBackground后,QGraphicsDropShadowEffect的外部阴影会被窗口的绘制边界直接裁剪掉——因为此时窗口只会渲染自身矩形范围内的内容,超出的阴影部分直接被丢弃了。下面给你两种靠谱的解决方案:
方案一:用父容器承载阴影(最简单易实现)
核心思路是把阴影效果放到一个透明的父容器上,你的无框窗口作为子窗口放在父容器中心,给阴影留出足够的显示空间。父容器负责渲染阴影,子窗口负责展示内容,完美避开裁剪问题。
修改你的main.cpp代码如下:
int main(int argc, char **argv) { QApplication app(argc, argv); // 1. 创建透明父容器,专门用来承载阴影 QWidget shadowContainer; shadowContainer.setAttribute(Qt::WA_TranslucentBackground); shadowContainer.setStyleSheet("background-color: transparent;"); // 2. 初始化你的无框窗口 MyWidget w; w.setParent(&shadowContainer); // 3. 给父容器添加阴影效果 QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect; effect->setBlurRadius(5); effect->setOffset(0, 0); effect->setColor(QColor(0, 0, 0, 100)); // 可自定义阴影颜色和透明度 shadowContainer.setGraphicsEffect(effect); // 4. 调整父容器大小,给阴影留出空间(模糊半径*2) const int shadowOffset = effect->blurRadius(); QSize containerSize = w.size() + QSize(shadowOffset * 2, shadowOffset * 2); shadowContainer.resize(containerSize); // 5. 把无框窗口居中放在父容器里 w.move(shadowOffset, shadowOffset); shadowContainer.show(); return app.exec(); }
这样修改后,你既能看到无框窗口的外部阴影,内部孔洞的阴影也不会受影响。
方案二:重写paintEvent手动绘制阴影
如果需要更自定义的阴影效果(比如不规则形状的窗口),可以自己在paintEvent里手动绘制阴影层。这种方法灵活性更高,不需要额外的父容器。
给你的MyWidget类添加paintEvent重写:
#include <QPainter> #include <QPainterPath> void MyWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿,让阴影更平滑 const int shadowRadius = 5; const QRect contentRect = rect().adjusted(shadowRadius, shadowRadius, -shadowRadius, -shadowRadius); // 第一步:绘制多层半透明矩形模拟模糊阴影 QColor shadowColor(0, 0, 0, 80); for (int i = 0; i < shadowRadius; ++i) { painter.setPen(Qt::NoPen); painter.setBrush(shadowColor); // 每次绘制的矩形轻微偏移,同时降低透明度 painter.drawRoundedRect(rect().adjusted(i, i, -i, -i), 5, 5); // 圆角窗口可调整圆角半径 shadowColor.setAlpha(qMax(0, shadowColor.alpha() - 15)); } // 第二步:绘制窗口主体背景 painter.setPen(Qt::NoPen); painter.setBrush(Qt::white); // 替换成你的窗口背景色 painter.drawRoundedRect(contentRect, 5, 5); // 和阴影的圆角保持一致 // 第三步:绘制你的窗口内部控件或内容 // ... 这里写你原来的绘制逻辑 ... }
记得要把MyWidget的初始大小设置为内容大小 + 阴影半径*2,比如内容是200x200,窗口大小就要设为210x210(阴影半径5),这样阴影才有足够的显示空间。
补充说明
为什么原来的方法不行?因为当你给MyWidget设置Qt::FramelessWindowHint和Qt::WA_TranslucentBackground后,窗口的可见区域被严格限制在自身的rect()范围内,QGraphicsDropShadowEffect生成的阴影超出了这个范围,所以被系统裁剪掉了。而父容器的方法相当于把阴影的绘制区域扩展到了更大的容器里,自然就能显示出来了。
内容的提问来源于stack exchange,提问作者arsdever

