代码来源: https://forum.qt.io/topic/77576/painting-shadow-around-a-parentless-qwidget/12
也可以参考:https://blog.csdn.net/qq_25532071/article/details/106098248
优点:使用此代码几乎没有多少CPU和内存的消耗。
效果:

代码如下
#pragma once#include <QtWidgets/qgraphicseffect.h>#include <QtGui/qpainter.h>#include <QtWidgets/qwidget.h>class Shadow : public QGraphicsEffect {public:enum Side {Left = 0x1,Right = 0x2,Bottom = 0x4,Top = 0x8,Around = Left | Top | Right | Bottom,};Q_DECLARE_FLAGS(Sides, Side);Shadow(QObject *parent = 0);Shadow(const QColor &c, qreal distance, qreal radius, Sides sides = Side::Around, QObject *parent = 0);Sides sides() const {return _side;}void setSides(Sides s) {_side = s;updateBoundingRect();}QColor color() const {return _color;}void setColor(const QColor &c) {_color = c;updateBoundingRect();}qreal blurRadius() const {return _blurRadius;}void setBlurRadius(qreal br) {_blurRadius = br;updateBoundingRect();}qreal distance() const {return _distance;}void setDistance(qreal d) {_distance = d;updateBoundingRect();}QRectF boundingRectFor(const QRectF &) const override;static QPixmap grab(QWidget *target, const QRect &rect, int offset); // Return a pixmap with target painted into it with margin = offset// Return a background blurred QImage to Draw as the widget's shadowstatic QImage paint(QWidget *target, const QRect &box, qreal radius, qreal distance, const QColor &c, Sides sides = Side::Around);protected:void draw(QPainter *painter) override;private:Sides _side;QColor _color;qreal _distance;qreal _blurRadius;};Q_DECLARE_OPERATORS_FOR_FLAGS(Shadow::Sides)
#include "shadow.h"Q_DECL_IMPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); // src/widgets/effects/qpixmapfilter.cppShadow::Shadow(QObject *parent) :QGraphicsEffect(parent) {}Shadow::Shadow(const QColor &c, qreal distance, qreal radius, Sides s, QObject *parent) : QGraphicsEffect(parent) {setColor(c);setBlurRadius(radius);setDistance(distance);setSides(s);}QRectF Shadow::boundingRectFor(const QRectF &r) const {qreal _delta = blurRadius() + distance();return r.marginsAdded(QMarginsF((sides() & Side::Left) ? _delta : 0,(sides() & Side::Top) ? _delta : 0,(sides() & Side::Right) ? _delta : 0,(sides() & Side::Bottom) ? _delta : 0));}void Shadow::draw(QPainter *painter) {if ((blurRadius() + distance()) <= 0) {drawSource(painter);return;}QPoint _offset;QPixmap _pixmap = sourcePixmap(Qt::DeviceCoordinates, &_offset, QGraphicsEffect::PadToEffectiveBoundingRect);if (_pixmap.isNull()) return;QTransform _transform = painter->worldTransform();painter->setWorldTransform(QTransform());QSize _backgroundSize = QSize(_pixmap.size().width() + 2 * distance(), _pixmap.size().height() + 2 * distance());QImage _temp(_backgroundSize, QImage::Format_ARGB32_Premultiplied);QPixmap scaled = _pixmap.scaled(_backgroundSize);_temp.fill(0);QPainter _tempPainter(&_temp);_tempPainter.setCompositionMode(QPainter::CompositionMode_Source);_tempPainter.drawPixmap(QPointF(-distance(), -distance()), scaled);_tempPainter.end();QImage blurred(_temp.size(), QImage::Format_ARGB32_Premultiplied);blurred.fill(0);QPainter blurPainter(&blurred);qt_blurImage(&blurPainter, _temp, blurRadius(), true, false);blurPainter.end();_temp = blurred;_tempPainter.begin(&_temp);_tempPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);_tempPainter.fillRect(_temp.rect(), color());_tempPainter.end();painter->drawImage(_offset, _temp);painter->drawPixmap(_offset, _pixmap, QRectF());painter->setWorldTransform(_transform);}QPixmap Shadow::grab(QWidget *target, const QRect &rect, int offset) {auto result = QPixmap(rect.size());auto r = rect.marginsRemoved(QMargins(offset, offset, offset, offset));result.fill(Qt::transparent);{QPainter p;p.begin(&result);target->render(&p, QPoint(offset, offset), r);p.end();}return result;}QImage Shadow::paint(QWidget *target, const QRect &box, qreal radius, qreal distance, const QColor &c, Sides sides) {const auto _source = grab(target, box, distance);if (_source.isNull() || distance <= 0) return QImage();QImage _backgroundImage(box.size(), QImage::Format_ARGB32_Premultiplied);_backgroundImage.fill(0);QPainter _backgroundPainter(&_backgroundImage);_backgroundPainter.drawPixmap(QPointF(), _source);_backgroundPainter.end();QImage blurredImage(_backgroundImage.size(), QImage::Format_ARGB32_Premultiplied);blurredImage.fill(0);{QPainter blurPainter(&blurredImage);qt_blurImage(&blurPainter, _backgroundImage, radius, true, false);blurPainter.end();}_backgroundImage = blurredImage;_backgroundPainter.begin(&_backgroundImage);_backgroundPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);auto margin = _backgroundImage.rect().marginsRemoved(QMargins((sides & Left) ? 0 : distance,(sides & Top) ? 0 : distance,(sides & Right) ? 0 : distance,(sides & Bottom) ? 0 : distance));_backgroundPainter.fillRect(margin, c);_backgroundPainter.end();return _backgroundImage;}
#pragma once#include "shadow.h"#include <QResizeEvent>class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent = 0) :QWidget(parent) {setShadowWidth(7);setWindowFlags(Qt::FramelessWindowHint);setAttribute(Qt::WA_TranslucentBackground, true);setAttribute(Qt::WA_NoSystemBackground, true);setStyleSheet("background:#FFFFFF;");setAutoFillBackground(false);}void setShadowWidth(int w) {_shWidth = w;//setContentsMargins(w, w, w, w);}int shadowWidth() const {return _shWidth;}void ensureLoaded() {if (_cashe.isNull()) {_cashe = QPixmap::fromImage(Shadow::paint(this, rect(), 18.0, shadowWidth(), QColor(0, 0, 0, 128)));}}protected:void paintEvent(QPaintEvent*e) override {QPainter painter(this);if (!_cashe.isNull()) painter.drawPixmap(QPoint(), _cashe);painter.setBrush(palette().background());painter.setPen(Qt::NoPen);painter.setRenderHint(QPainter::Antialiasing, true);auto m = QMargins(shadowWidth(), shadowWidth(), shadowWidth(), shadowWidth());painter.drawRoundedRect(rect().marginsRemoved(m), 2.0, 2.0);painter.setRenderHint(QPainter::Antialiasing, false);}void resizeEvent(QResizeEvent *e) {ensureLoaded();_cashe = _cashe.scaled(e->size());QWidget::resizeEvent(e);}private:QPixmap _cashe = QPixmap();int _shWidth = 0;};
#include "widget.h"
#include "mainwindow.h"#include <QApplication>#include "widget.h"int main(int argc, char *argv[]){QApplication a(argc, argv);Widget *widget = new Widget;widget->resize(800, 800);widget->show();return a.exec();}
