How to make a QGraphicsItem show the background in a QGraphicsScene?
Asked Answered
U

1

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.

Ubald answered 15/6, 2012 at 17:6 Comment(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: mediachance.com/realdraw/help/index.html?pushback.htmGoldshell
W
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();
}
Wil answered 15/6, 2012 at 20:30 Comment(6)
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.Ubald
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?Ubald
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!Wil
Thanks again Dave. I'll keep working on this in the meantime.Ubald
Edit made. This should work now. (At least it does on my machine!)Wil
Dave, your Qt skills are god tier. Thank you.Ubald

© 2022 - 2024 — McMap. All rights reserved.