代码来源: https://forum.qt.io/topic/77576/painting-shadow-around-a-parentless-qwidget/12
    也可以参考:https://blog.csdn.net/qq_25532071/article/details/106098248

    优点:使用此代码几乎没有多少CPU和内存的消耗。
    image.png
    效果:
    image.png
    image.png

    代码如下

    1. #pragma once
    2. #include <QtWidgets/qgraphicseffect.h>
    3. #include <QtGui/qpainter.h>
    4. #include <QtWidgets/qwidget.h>
    5. class Shadow : public QGraphicsEffect {
    6. public:
    7. enum Side {
    8. Left = 0x1,
    9. Right = 0x2,
    10. Bottom = 0x4,
    11. Top = 0x8,
    12. Around = Left | Top | Right | Bottom,
    13. };
    14. Q_DECLARE_FLAGS(Sides, Side);
    15. Shadow(QObject *parent = 0);
    16. Shadow(const QColor &c, qreal distance, qreal radius, Sides sides = Side::Around, QObject *parent = 0);
    17. Sides sides() const {
    18. return _side;
    19. }
    20. void setSides(Sides s) {
    21. _side = s;
    22. updateBoundingRect();
    23. }
    24. QColor color() const {
    25. return _color;
    26. }
    27. void setColor(const QColor &c) {
    28. _color = c;
    29. updateBoundingRect();
    30. }
    31. qreal blurRadius() const {
    32. return _blurRadius;
    33. }
    34. void setBlurRadius(qreal br) {
    35. _blurRadius = br;
    36. updateBoundingRect();
    37. }
    38. qreal distance() const {
    39. return _distance;
    40. }
    41. void setDistance(qreal d) {
    42. _distance = d;
    43. updateBoundingRect();
    44. }
    45. QRectF boundingRectFor(const QRectF &) const override;
    46. static QPixmap grab(QWidget *target, const QRect &rect, int offset); // Return a pixmap with target painted into it with margin = offset
    47. // Return a background blurred QImage to Draw as the widget's shadow
    48. static QImage paint(QWidget *target, const QRect &box, qreal radius, qreal distance, const QColor &c, Sides sides = Side::Around);
    49. protected:
    50. void draw(QPainter *painter) override;
    51. private:
    52. Sides _side;
    53. QColor _color;
    54. qreal _distance;
    55. qreal _blurRadius;
    56. };
    57. Q_DECLARE_OPERATORS_FOR_FLAGS(Shadow::Sides)
    1. #include "shadow.h"
    2. Q_DECL_IMPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); // src/widgets/effects/qpixmapfilter.cpp
    3. Shadow::Shadow(QObject *parent) :QGraphicsEffect(parent) {
    4. }
    5. Shadow::Shadow(const QColor &c, qreal distance, qreal radius, Sides s, QObject *parent) : QGraphicsEffect(parent) {
    6. setColor(c);
    7. setBlurRadius(radius);
    8. setDistance(distance);
    9. setSides(s);
    10. }
    11. QRectF Shadow::boundingRectFor(const QRectF &r) const {
    12. qreal _delta = blurRadius() + distance();
    13. return r.marginsAdded(QMarginsF(
    14. (sides() & Side::Left) ? _delta : 0,
    15. (sides() & Side::Top) ? _delta : 0,
    16. (sides() & Side::Right) ? _delta : 0,
    17. (sides() & Side::Bottom) ? _delta : 0
    18. ));
    19. }
    20. void Shadow::draw(QPainter *painter) {
    21. if ((blurRadius() + distance()) <= 0) {
    22. drawSource(painter);
    23. return;
    24. }
    25. QPoint _offset;
    26. QPixmap _pixmap = sourcePixmap(Qt::DeviceCoordinates, &_offset, QGraphicsEffect::PadToEffectiveBoundingRect);
    27. if (_pixmap.isNull()) return;
    28. QTransform _transform = painter->worldTransform();
    29. painter->setWorldTransform(QTransform());
    30. QSize _backgroundSize = QSize(_pixmap.size().width() + 2 * distance(), _pixmap.size().height() + 2 * distance());
    31. QImage _temp(_backgroundSize, QImage::Format_ARGB32_Premultiplied);
    32. QPixmap scaled = _pixmap.scaled(_backgroundSize);
    33. _temp.fill(0);
    34. QPainter _tempPainter(&_temp);
    35. _tempPainter.setCompositionMode(QPainter::CompositionMode_Source);
    36. _tempPainter.drawPixmap(QPointF(-distance(), -distance()), scaled);
    37. _tempPainter.end();
    38. QImage blurred(_temp.size(), QImage::Format_ARGB32_Premultiplied);
    39. blurred.fill(0);
    40. QPainter blurPainter(&blurred);
    41. qt_blurImage(&blurPainter, _temp, blurRadius(), true, false);
    42. blurPainter.end();
    43. _temp = blurred;
    44. _tempPainter.begin(&_temp);
    45. _tempPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
    46. _tempPainter.fillRect(_temp.rect(), color());
    47. _tempPainter.end();
    48. painter->drawImage(_offset, _temp);
    49. painter->drawPixmap(_offset, _pixmap, QRectF());
    50. painter->setWorldTransform(_transform);
    51. }
    52. QPixmap Shadow::grab(QWidget *target, const QRect &rect, int offset) {
    53. auto result = QPixmap(rect.size());
    54. auto r = rect.marginsRemoved(QMargins(offset, offset, offset, offset));
    55. result.fill(Qt::transparent);
    56. {
    57. QPainter p;
    58. p.begin(&result);
    59. target->render(&p, QPoint(offset, offset), r);
    60. p.end();
    61. }
    62. return result;
    63. }
    64. QImage Shadow::paint(QWidget *target, const QRect &box, qreal radius, qreal distance, const QColor &c, Sides sides) {
    65. const auto _source = grab(target, box, distance);
    66. if (_source.isNull() || distance <= 0) return QImage();
    67. QImage _backgroundImage(box.size(), QImage::Format_ARGB32_Premultiplied);
    68. _backgroundImage.fill(0);
    69. QPainter _backgroundPainter(&_backgroundImage);
    70. _backgroundPainter.drawPixmap(QPointF(), _source);
    71. _backgroundPainter.end();
    72. QImage blurredImage(_backgroundImage.size(), QImage::Format_ARGB32_Premultiplied);
    73. blurredImage.fill(0);
    74. {
    75. QPainter blurPainter(&blurredImage);
    76. qt_blurImage(&blurPainter, _backgroundImage, radius, true, false);
    77. blurPainter.end();
    78. }
    79. _backgroundImage = blurredImage;
    80. _backgroundPainter.begin(&_backgroundImage);
    81. _backgroundPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
    82. auto margin = _backgroundImage.rect().marginsRemoved(QMargins(
    83. (sides & Left) ? 0 : distance,
    84. (sides & Top) ? 0 : distance,
    85. (sides & Right) ? 0 : distance,
    86. (sides & Bottom) ? 0 : distance
    87. ));
    88. _backgroundPainter.fillRect(margin, c);
    89. _backgroundPainter.end();
    90. return _backgroundImage;
    91. }
    1. #pragma once
    2. #include "shadow.h"
    3. #include <QResizeEvent>
    4. class Widget : public QWidget {
    5. Q_OBJECT
    6. public:
    7. Widget(QWidget *parent = 0) :QWidget(parent) {
    8. setShadowWidth(7);
    9. setWindowFlags(Qt::FramelessWindowHint);
    10. setAttribute(Qt::WA_TranslucentBackground, true);
    11. setAttribute(Qt::WA_NoSystemBackground, true);
    12. setStyleSheet("background:#FFFFFF;");
    13. setAutoFillBackground(false);
    14. }
    15. void setShadowWidth(int w) {
    16. _shWidth = w;
    17. //setContentsMargins(w, w, w, w);
    18. }
    19. int shadowWidth() const {
    20. return _shWidth;
    21. }
    22. void ensureLoaded() {
    23. if (_cashe.isNull()) {
    24. _cashe = QPixmap::fromImage(Shadow::paint(this, rect(), 18.0, shadowWidth(), QColor(0, 0, 0, 128)));
    25. }
    26. }
    27. protected:
    28. void paintEvent(QPaintEvent*e) override {
    29. QPainter painter(this);
    30. if (!_cashe.isNull()) painter.drawPixmap(QPoint(), _cashe);
    31. painter.setBrush(palette().background());
    32. painter.setPen(Qt::NoPen);
    33. painter.setRenderHint(QPainter::Antialiasing, true);
    34. auto m = QMargins(shadowWidth(), shadowWidth(), shadowWidth(), shadowWidth());
    35. painter.drawRoundedRect(rect().marginsRemoved(m), 2.0, 2.0);
    36. painter.setRenderHint(QPainter::Antialiasing, false);
    37. }
    38. void resizeEvent(QResizeEvent *e) {
    39. ensureLoaded();
    40. _cashe = _cashe.scaled(e->size());
    41. QWidget::resizeEvent(e);
    42. }
    43. private:
    44. QPixmap _cashe = QPixmap();
    45. int _shWidth = 0;
    46. };
    1. #include "widget.h"
    1. #include "mainwindow.h"
    2. #include <QApplication>
    3. #include "widget.h"
    4. int main(int argc, char *argv[])
    5. {
    6. QApplication a(argc, argv);
    7. Widget *widget = new Widget;
    8. widget->resize(800, 800);
    9. widget->show();
    10. return a.exec();
    11. }