PDA

View Full Version : Changing a QGraphicsItem's transparency as a whole



blooglet
10th April 2011, 20:02
I'm trying to change the transparency of this QGraphicsItem-inheriting shape.

http://i.imgur.com/6XYkX.png

Because of the way the drop shadow is drawn, this happens when I change the object's transparency...

http://i.imgur.com/s9IvE.png

I've thought about only drawing the parts of the shadow that will be seen, but I don't think that will work with these shapes:

http://i.imgur.com/02Uhf.png

I want the graphics item to be drawn and then have the opacity be changed on the resulting drawing, thus avoiding that you can see the full drop shadow through the shape itself. How can I change the QGraphicsItem's transparency as a whole?

stampede
11th April 2011, 10:02
QPainter's opacity is applied to all drawing operations individually, so simple item->setOpacity(x) is not enough.
I have two ideas, first is to erase the area where you have rendered shadow under the item ( by setting QPainter's brush to QPainter::background() ), but this will erase everything you have rendered, including other items rendered under current item. Code sample (paint method for simple ellipse item):


void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ){
const qreal opaque = 1.0;
const qreal transparent = 0.5;
const QRect shadow(10,10,100,100);
const QRect item(0,0,100,100);

painter->setOpacity( transparent );
painter->setBrush( QBrush(Qt::black) );
painter->drawEllipse( shadow );

painter->setOpacity( opaque );
painter->setBrush( painter->background() );
painter->drawEllipse( item );


painter->setOpacity( transparent );
painter->setBrush( QBrush(Qt::red) );
painter->drawEllipse( item );
}

Another way is to set proper clip path for painter when rendering shadow, so it will not be rendered in the area under item. This solution will not erase background items.
Idea is to let the painter draw shadow everywhere except the item's area, so I have used QPainterPath::subtracted to create proper painting area (compilable sample):


#include <QtCore>
#include <QtGui>

class RectItem : public QGraphicsItem{
public:
RectItem( QGraphicsItem * parent = NULL ) : QGraphicsItem(parent){

}
QRectF boundingRect() const{
return QRectF(0,0,150,150);
}
void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ){
const qreal transparent = 0.5;
const QRect shadow(10,10,100,100);
const QRect item(0,0,100,100);

QPainterPath scene; scene.addRect(0,0,300,300); // scene rectangle, hard coded here, but its just an example
QPainterPath shadowp; shadowp.addEllipse(item);
shadowp = scene.subtracted(shadowp); // can render everywhere except item's area
painter->setClipPath(shadowp);

painter->setOpacity( transparent );
painter->setBrush( QBrush(Qt::black) );
painter->drawEllipse( shadow );

QPainterPath itemPath; itemPath.addEllipse(item);
painter->setClipPath(itemPath); // you can use setClipPath(scene) as well

painter->setBrush( QBrush(Qt::red) );
painter->drawEllipse( item );
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

QGraphicsView * view = new QGraphicsView();
QGraphicsScene * scene = new QGraphicsScene(QRectF(0,0,300,300));
view->setScene(scene);
view->show();
RectItem * rect = new RectItem();
rect->setPos(20,20);
RectItem * rect2 = new RectItem();
rect2->setPos(40,50);
scene->addItem(rect);
scene->addItem(rect2);

return a.exec();
}

I think this should be what you are looking for. Probably you can use your item's shape() method instead of create QPainterPath by hand, but this sample is just to present the idea. Hope it helps.

blooglet
11th April 2011, 10:18
I used the second method. It works perfectly. Thanks!

http://i.imgur.com/mfaR6.png

blooglet
25th April 2011, 16:53
(Apologies for digging up this older topic, but it's directly related to this issue.)

I've noticed that this technique produces bad drawing results on some shapes, likely due to rounding:

http://i.imgur.com/5InBz.png

I suspect that this may have something to do with the problem:


QPainterPath::subtracted

Returns a path which is p's fill area subtracted from this path's fill area.
Set operations on paths will treat the paths as areas. Non-closed paths will be treated as implicitly closed. Bezier curves may be flattened to line segments due to numerical instability of doing bezier curve intersections.

This function was introduced in Qt 4.3.

How do I fix this?

blooglet
5th May 2011, 14:19
Bumped topic