Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Painting shadow around a parentless QWidget
Forum Updated to NodeBB v4.3 + New Features

Painting shadow around a parentless QWidget

Scheduled Pinned Locked Moved Solved General and Desktop
12 Posts 4 Posters 9.0k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • IMAN4KI Offline
    IMAN4KI Offline
    IMAN4K
    wrote on last edited by
    #1

    I'm trying to have a nice shadow effect around my QWidget by drawing that inside widget's coordinate
    Could you please put me on the right way to do that ?
    like this one:
    enter image description here

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      Is QGraphicsDropShadowEffect what you are looking for ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      IMAN4KI 1 Reply Last reply
      1
      • SGaistS SGaist

        Hi,

        Is QGraphicsDropShadowEffect what you are looking for ?

        IMAN4KI Offline
        IMAN4KI Offline
        IMAN4K
        wrote on last edited by IMAN4K
        #3

        @SGaist
        Hi.
        No actually all QGraphicsEffect require a parent or source to paint the effects! (it's useless in case of menus)
        and what is needed is to paint the shadow inside the widget's area (faking the shadow).
        Let's say we want to have 5dp shadow surrounding the widget like this:

        enter image description here

        So what should be used inside paintEvent ?

        1 Reply Last reply
        0
        • IMAN4KI Offline
          IMAN4KI Offline
          IMAN4K
          wrote on last edited by IMAN4K
          #4

          @SGaist
          There are two possibility in my mind :
          1.Render shadow on QPixmap (one pixmap per each edge.r.g right, top, bottomleft, ...) and then draw these cashed pixmaps on desired region of widget
          2.Use QLinearGradient to draw (corners are the problem)
          There are no workaround out there! that's wierd

          1 Reply Last reply
          0
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #5

            Something's not completely clear. Can you show how you setup your widgets ?

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            IMAN4KI 1 Reply Last reply
            1
            • SGaistS SGaist

              Something's not completely clear. Can you show how you setup your widgets ?

              IMAN4KI Offline
              IMAN4KI Offline
              IMAN4K
              wrote on last edited by IMAN4K
              #6

              @SGaist
              There is no Widgets.
              It's just one simple transparent widget that we want it to have shadow (there is only one widget in that image i have mentioned):

              #define WIDTH 5
              
              class ShadowWidget : public QWidget {
              	Q_OBJECT
              
              public:
              	ShadowWidget(QWidget *parent = 0) :QWidget(parent) {
              		setWindowFlags(Qt::FramelessWindowHint);
              		setAttribute(Qt::WA_TranslucentBackground, true);
              	}
              
              protected:
              	void paintEvent(QPaintEvent *) override {
              		QPainter p(this);
              
              		/*
              			draw the shadow here
              		*/
              
              		auto r = rect().marginsRemoved(QMargins(WIDTH, WIDTH, WIDTH, WIDTH));
              		p.setClipRect(r);
              		p.setBrush(palette().background());
              		p.setPen(Qt::NoPen);
              		p.drawRect(r);
              	}
              };
              
              J.HilkJ 1 Reply Last reply
              0
              • IMAN4KI IMAN4K

                @SGaist
                There is no Widgets.
                It's just one simple transparent widget that we want it to have shadow (there is only one widget in that image i have mentioned):

                #define WIDTH 5
                
                class ShadowWidget : public QWidget {
                	Q_OBJECT
                
                public:
                	ShadowWidget(QWidget *parent = 0) :QWidget(parent) {
                		setWindowFlags(Qt::FramelessWindowHint);
                		setAttribute(Qt::WA_TranslucentBackground, true);
                	}
                
                protected:
                	void paintEvent(QPaintEvent *) override {
                		QPainter p(this);
                
                		/*
                			draw the shadow here
                		*/
                
                		auto r = rect().marginsRemoved(QMargins(WIDTH, WIDTH, WIDTH, WIDTH));
                		p.setClipRect(r);
                		p.setBrush(palette().background());
                		p.setPen(Qt::NoPen);
                		p.drawRect(r);
                	}
                };
                
                J.HilkJ Offline
                J.HilkJ Offline
                J.Hilk
                Moderators
                wrote on last edited by
                #7

                @IMAN4K

                hi, there's still a Widget that can a have a QGraphicsDropShadowEffect, doesn't it? In your case you can access it with this

                also I hade some similar issues in this thread
                maybe it can help you.


                Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                Q: What's that?
                A: It's blue light.
                Q: What does it do?
                A: It turns blue.

                IMAN4KI 1 Reply Last reply
                1
                • J.HilkJ J.Hilk

                  @IMAN4K

                  hi, there's still a Widget that can a have a QGraphicsDropShadowEffect, doesn't it? In your case you can access it with this

                  also I hade some similar issues in this thread
                  maybe it can help you.

                  IMAN4KI Offline
                  IMAN4KI Offline
                  IMAN4K
                  wrote on last edited by
                  #8

                  @J.Hilk
                  Hi.

                  hi, there's still a Widget that can a have a QGraphicsDropShadowEffect, doesn't it?

                  You mean we put a widget inside another (transparent) one and set QGraphicsEffect on first one ?
                  (If yes then we have overhead of two widget)
                  And about that thread, how exactly drawShadow(..) draw a shadow?
                  it didn't work

                  1 Reply Last reply
                  0
                  • IMAN4KI Offline
                    IMAN4KI Offline
                    IMAN4K
                    wrote on last edited by IMAN4K
                    #9

                    There is a code snippet that works for a custom QGraphicsEffect (QGraphicsEffect::draw(QPainter * painter)) :

                    if ((blurRadius() + distance()) <= 0) {
                    			drawSource(painter);
                    			return;
                    		}
                    
                    		PixmapPadMode _mode = QGraphicsEffect::PadToEffectiveBoundingRect;
                    		QPoint _offset;
                    		QPixmap _pixmap = sourcePixmap(Qt::DeviceCoordinates, &_offset, _mode);
                    		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(), false, true);
                    		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);
                    

                    But i can not use it for simply drawing inside a widget. don't know how to produce sourcePixmap(..)
                    If you can understand it correctly please help me to figure this out.
                    It produce this nice result:
                    enter image description here

                    jsulmJ 1 Reply Last reply
                    0
                    • IMAN4KI IMAN4K

                      There is a code snippet that works for a custom QGraphicsEffect (QGraphicsEffect::draw(QPainter * painter)) :

                      if ((blurRadius() + distance()) <= 0) {
                      			drawSource(painter);
                      			return;
                      		}
                      
                      		PixmapPadMode _mode = QGraphicsEffect::PadToEffectiveBoundingRect;
                      		QPoint _offset;
                      		QPixmap _pixmap = sourcePixmap(Qt::DeviceCoordinates, &_offset, _mode);
                      		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(), false, true);
                      		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);
                      

                      But i can not use it for simply drawing inside a widget. don't know how to produce sourcePixmap(..)
                      If you can understand it correctly please help me to figure this out.
                      It produce this nice result:
                      enter image description here

                      jsulmJ Offline
                      jsulmJ Offline
                      jsulm
                      Lifetime Qt Champion
                      wrote on last edited by
                      #10

                      @IMAN4K http://doc.qt.io/qt-5.8/qgraphicseffect.html#sourcePixmap

                      https://forum.qt.io/topic/113070/qt-code-of-conduct

                      IMAN4KI 1 Reply Last reply
                      1
                      • jsulmJ jsulm

                        @IMAN4K http://doc.qt.io/qt-5.8/qgraphicseffect.html#sourcePixmap

                        IMAN4KI Offline
                        IMAN4KI Offline
                        IMAN4K
                        wrote on last edited by IMAN4K
                        #11

                        @jsulm
                        Definitely i read that before :)
                        But the doc says :

                        Calling this function with Qt::DeviceCoordinates outside of QGraphicsEffect::draw() will give undefined results, as there is no device context available.

                        Another thing is sourcePixmap(..) is a protected function and work if our widget has a parent widget while i need a static function like this (that operate on widget paintEvent()) :
                        static void paintShadow(QPainter *, qreal radius, qreal distance,.....)
                        And i couldn't find the source code for sourcepixmap()
                        We just need to produce this kind of blur image (if we remove source painting):
                        enter image description here

                        I tried to get the pixmap via this function :

                        static QPixmap grab(QWidget *target, const QRect &rect) {
                        		auto result = QPixmap(rect.size());
                        		result.fill(Qt::transparent);
                        		{
                        			QPainter p;
                        			p.begin(&result);
                        			target->render(&p, QPoint(rect.left(), rect.top()), rect, QWidget::DrawChildren | QWidget::IgnoreMask);
                        			p.end();
                        		}
                        		return result;
                        	}
                        

                        But if i put the result in place of _pixmap it doesn't draw the blur image

                        1 Reply Last reply
                        0
                        • IMAN4KI Offline
                          IMAN4KI Offline
                          IMAN4K
                          wrote on last edited by IMAN4K
                          #12

                          Finally figured it out.
                          This will work for both parentless QWidget and normal Widget with parent.for parentless Widget use it inside paintEvent():
                          enter image description here

                          shadow.h:

                          #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)
                          
                          

                          shadow.cpp:

                          #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;
                          }
                          
                          

                          main.cpp:

                          #include <QtWidgets/qapplication.h>
                          #include "shadow.h"
                          
                          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;
                          };
                          
                          int main(int argc, char *argv[]) {
                              QApplication app(argc, argv);
                              Widget *widget = new Widget;
                              widget->resize(200, 200);
                              widget->show();
                              return app.exec();
                          }
                          
                          #include "main.moc"
                          

                          Result for borderless QWidget (without parent):

                          enter image description here

                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved