7

In a QGraphicsScene, I have a background set with a few QGraphicsItems on top of it. These graphics items are of arbitrary shape. I'd like to make another QGraphicsItem, i.e. a circle, which when placed over these items will essentially show the background within this circle, instead of being filled with a color.

It would sort of be like having a background with multiple layers on top of it in photoshop. Then, using a circular marquee tool to delete all the layers on top of the background to show the background within the circle.

Or, another way to view it could be to have an opacity set, but this opacity affects items directly underneath it (but only within the ellipse) to show the background.

JustinBlaber
  • 4,629
  • 2
  • 36
  • 57
  • 1
    A program I've used called Real-Draw generalized this with what is called a "push back" object. Quite useful, you might consider using a similar generalization: http://www.mediachance.com/realdraw/help/index.html?pushback.htm – HostileFork says dont trust SE Jun 17 '12 at 00:05

1 Answers1

7

The following might work. It basically extends a normal QGraphicsScene with the ability to only render its background to any QPainter. Then, your "cut out" graphics item just renders the scene background over top of the other items. For this to work, the cut out item would have to have the highest Z-value.

screen shot

#include <QtGui>

class BackgroundDrawingScene : public QGraphicsScene {
public:
  explicit BackgroundDrawingScene() : QGraphicsScene() {}
  void renderBackground(QPainter *painter,
                        const QRectF &source,
                        const QRectF &target) {
    painter->save();
    painter->setWorldTransform(
          QTransform::fromTranslate(target.left() - source.left(),
                                    target.top() - source.top()),
          true);
    QGraphicsScene::drawBackground(painter, source);
    painter->restore();
  }
};

class CutOutGraphicsItem : public QGraphicsEllipseItem {
public:
  explicit CutOutGraphicsItem(const QRectF &rect)
    : QGraphicsEllipseItem(rect) {
    setFlag(QGraphicsItem::ItemIsMovable);
  }
protected:
  void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) {
    BackgroundDrawingScene *bgscene =
        dynamic_cast<BackgroundDrawingScene*>(scene());
    if (!bgscene) {
      return;
    }

    painter->setClipPath(shape());
    bgscene->renderBackground(painter,
                              mapToScene(boundingRect()).boundingRect(),
                              boundingRect());
  }
};


int main(int argc, char **argv) {
  QApplication app(argc, argv);

  BackgroundDrawingScene scene;
  QRadialGradient gradient(0, 0, 10);
  gradient.setSpread(QGradient::RepeatSpread);
  scene.setBackgroundBrush(gradient);

  scene.addRect(10., 10., 100., 50., QPen(Qt::SolidLine), QBrush(Qt::red));
  scene.addItem(new CutOutGraphicsItem(QRectF(20., 20., 20., 20.)));

  QGraphicsView view(&scene);
  view.show();

  return app.exec();
}
Dave Mateer
  • 17,608
  • 15
  • 96
  • 149
  • Dave, thanks a lot. This code is pretty interesting. Can you modify it to allow the ellipse to move? I got close by using the `setFlag(QGraphicsItem::ItemIsMovable,true) and setFlag(QGraphicsItem::ITemIsSelectable,true)` and also used the `mapToScene(shape())` and `mapToScene(rect())` before sending those parameters to the `renderBackground` function, but it it's a little off. I'll study your code in more detail later tonight. Thanks again. – JustinBlaber Jun 15 '12 at 22:05
  • Ok so I'm still not having any luck... When I do the above it seems to translate the background away from the item because the `paint` function uses item coordinates which do not change when the item is dragged. Is there a way to translate the painter object after calling the `renderBackground` function? – JustinBlaber Jun 16 '12 at 22:20
  • Oh yeah, I see the problem. The code above is not handling the position of the item. (You would probably get the same problem if you call `setPos()` on the item before adding it to the scene.) I'm not in a place to test any code right now, but if you have the source code for Qt, check the `QGraphicsScene::render()` method. I bet they are temporarily reversing the transform on the painter, doing the paint, then restoring the transform. Sorry I can't work on it right now. If you're still stuck, I'll try to post a solution tomorrow or Monday! – Dave Mateer Jun 16 '12 at 23:05
  • Thanks again Dave. I'll keep working on this in the meantime. – JustinBlaber Jun 16 '12 at 23:32
  • Edit made. This should work now. (At least it does on my machine!) – Dave Mateer Jun 16 '12 at 23:58
  • 1
    Dave, your Qt skills are god tier. Thank you. – JustinBlaber Jun 17 '12 at 00:06