3

I've been trying to use QGraphicsItemGroup to get the bounding rectangle of a group of QGraphicsItem*s. It appears to me that the bounding rectangle is correctly determined when I insert all of the items into the group; but if I then move items in the group, the bounding rectangle does not update to contain the moved items as I expect. I cannot find indication in the Documentation as to whether what I'm seeing is correct behavior or not; my guess is that I'm either misunderstanding how the QGraphicsItemGroup works or misusing it

An example that I've been using to test:

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QTransform>
#include <QGraphicsEllipseItem>
#include <QDebug>
#include <QTimer>

#include <cmath>

const double pi = 3.14;

QTransform rotation(double degrees)
{

    double a    = pi/180 * degrees;
    double sina = sin(a);
    double cosa = cos(a);

    return QTransform(cosa, sina, -sina, cosa, 0, 0);
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    scene = new QGraphicsScene(this);
    ui->graphicsView->setScene(scene);
    ui->graphicsView->show();

    //Shouldn't execute until after window.show() and application.exec()
    //are called
    QTimer::singleShot(1, this, &MainWindow::build);
}

void MainWindow::build()
{
    QGraphicsEllipseItem* el1 = scene->addEllipse(-5, -5, 10, 10);
    scene->addLine(0, 0, 0, 10)->setParentItem(el1);

    QGraphicsEllipseItem* el2 = scene->addEllipse(-5, -5, 10, 10);
    scene->addLine(0, 0, 0, 10)->setParentItem(el2);

    QGraphicsEllipseItem* el3 = scene->addEllipse(-5, -5, 10, 10);
    scene->addLine(0, 0, 0, 10)->setParentItem(el3);

    QGraphicsEllipseItem* el4 = scene->addEllipse(-5, -5, 10, 10);
    scene->addLine(0, 0, 0, 10)->setParentItem(el4);

    QGraphicsItemGroup* group = new QGraphicsItemGroup;
    group->addToGroup(el1);
    group->addToGroup(el2);
    group->addToGroup(el3);
    group->addToGroup(el4);

    scene->addItem(group);

    scene->addRect(group->boundingRect());

    QTransform translate2(1, 0, 0, 1, 10, 10);
    QTransform t2 = /*rotation(45) **/ translate2;
    el2->setTransform(t2);

    QTransform translate3(1, 0, 0, 1, 20, 20);
    QTransform t3 = /*rotation(-45) **/ translate3 * t2;
    el3->setTransform(t3);

    QTransform translate4(1, 0, 0, 1, 20, -20);
    QTransform t4 = translate4 * t3;
    el4->setTransform(t4);

    qDebug() << t4.dx() << t4.dy() << atan2(t4.m12(), t4.m22())*180/pi;
    QTransform t4i = t4.inverted();
    qDebug() << t4i.dx() << t4i.dy() << atan2(t4i.m12(), t4i.m22())*180/pi;

    scene->addRect(group->boundingRect());
}

MainWindow::~MainWindow()
{
    delete ui;
}

The final scene displayed looks like this

Actual outcome

But, I was expecting something like this, with a small box from the first boundingRect, and a larger one for the second

Expected outcome

Am I misunderstanding how the QGraphicsItemGroup works, or am I using it incorrectly?

Qt Version: 5.10

OS: Ubuntu 16.04

Compiler GCC 5.4

Ipiano
  • 234
  • 1
  • 8

2 Answers2

1

The boundingrect is only updated at additem. Therefore the method childrenBoundingRect() must be used instead of boundingRect(). In a separate subclass of QGraphicsItemGroup say MyQGraphicsItemGroup we can overload the method virtual QRectF boundingRect() const override so that childrenBoundingRect() is called.

QRectF MyQGraphicsItemGroup::boundingRect() const
{
   // must be overloaded, otherwise the boundingrect will only be actualized on
   // additem is actualized. This leads to the fact that the boundingrect 
   // will not close around the word items after e.g., moving them. 
   return childrenBoundingRect();
}
0

The QGraphicsItemGroup documentation doesn't seem to mention this, but it only recalculates the boundingRect for a QGraphicsItemGroup after the view and QGraphicsScene is shown.

Adding the items afterwards

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QGraphicsScene scene;
    QGraphicsView mainView(&scene);
    scene.setSceneRect(0, 0, 800, 800);

    QGraphicsEllipseItem* el1 = scene.addEllipse(-5, -5, 10, 10);
    scene.addLine(0, 0, 0, 10)->setParentItem(el1);

    QGraphicsEllipseItem* el2 = scene.addEllipse(-5, -5, 10, 10);
    scene.addLine(0, 0, 0, 10)->setParentItem(el2);

    QGraphicsEllipseItem* el3 = scene.addEllipse(-5, -5, 10, 10);
    scene.addLine(0, 0, 0, 10)->setParentItem(el3);

    QGraphicsEllipseItem* el4 = scene.addEllipse(-5, -5, 10, 10);
    scene.addLine(0, 0, 0, 10)->setParentItem(el4);

    QGraphicsItemGroup* group = new QGraphicsItemGroup;
    scene.addItem(group);


    QTransform translate2(1, 0, 0, 1, 10, 10);
    QTransform t2 = /*rotation(45) **/ translate2;
    el2->setTransform(t2);

    QTransform translate3(1, 0, 0, 1, 20, 20);
    QTransform t3 = /*rotation(-45) **/ translate3 * t2;
    el3->setTransform(t3);

    QTransform translate4(1, 0, 0, 1, 20, -20);
    QTransform t4 = translate4 * t3;
    el4->setTransform(t4);

    mainView.show();

    group->addToGroup(el1);
    group->addToGroup(el2);
    group->addToGroup(el3);
    group->addToGroup(el4);
    scene.addRect(group->boundingRect());
    qDebug() << group->sceneBoundingRect() << endl << group->boundingRect();
    scene.addRect(group->sceneBoundingRect());

    return a.exec();
}

Results in the bounding rect of all members of the group

enter image description here

TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85
  • Interesting, what is the context of MainView here? I've got my QGraphicsViewWidget as a child of MainWindow; created with a Qt Form .ui file. Even after adjusting so everything is added after MainWindow.show(), this is not what I see. Could the behavior be different when the QGraphicsView has a parent widget? – Ipiano Mar 08 '18 at 17:05
  • Ok, I'll have to do some experimenting then; my guess is that show() doesn't get called when the ViewWidget isn't a top-level object; so maybe something gets initialized differently? I have found that childrenBoundingRect() does what I want; that one goes through all decendents of the GraphicsItem and combines their bounds. Unfortunately, that's not stored when the rects change; so it's a linear operation on the number of descendents each time. Good news is that none of my items have more than 10 descendents or so – Ipiano Mar 10 '18 at 16:47