代码来源: 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 shadow
static 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.cpp
Shadow::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_OBJECT
public:
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();
}