PDA

View Full Version : Question about QGraphicsRectItem



lni
28th January 2009, 04:43
Hi,

I have an outer QGraphicsRectItem and inner QGraphicsRectItem, where the outerRec is the parent of innerRect. When I do setRect, I would expect the rect is in respect to its parent item, but it is not the case, as shown in the example, where the red innerRect is outside its container and its position is related to the scene. Is this a bug? If it is a feature, then why not use setSize + translate? Currently the outerRect's rect has no meaning to the innerRect..



#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>

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

QGraphicsScene scene;
scene.setSceneRect( 0, 0, 500, 500 );

QGraphicsRectItem outerRect;
outerRect.setRect( QRectF( 20, 20, 400, 400 ) );

QGraphicsRectItem innerRect;
innerRect.setRect( QRectF( 0, 0, 200, 400 ) );
innerRect.setParentItem( &outerRect );
innerRect.setPen( QPen( Qt::red ) );

scene.addItem( &outerRect );

QGraphicsView view(&scene);

view.show();

return app.exec();

}

seneca
28th January 2009, 09:27
What happens if you set the geometry of the second rectangle after making it child of the first?

wysota
28th January 2009, 09:31
The code works correctly. setRect() only sets the coordinate system of an item. You have to then position the item using setPos(), otherwise the child item is put in such a place so that its (0,0) point matches the (0,0) point of its parent which is exactly the effect you observe.

lni
28th January 2009, 16:46
The code works correctly. setRect() only sets the coordinate system of an item. You have to then position the item using setPos(), otherwise the child item is put in such a place so that its (0,0) point matches the (0,0) point of its parent which is exactly the effect you observe.

Try this, you got inconsistent behavior:


#include <QApplication>
#include <QWidget>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QGroupBox>
#include <QFrame>

static void testWidgetRect()
{
QWidget* root = new QWidget;
root->setGeometry(QRect(0, 0, 500, 500));

QPalette palette;
QBrush brush( Qt::white );
palette.setBrush(QPalette::Inactive, QPalette::Window, brush);
palette.setBrush(QPalette::Active, QPalette::Window, brush);
root->setPalette(palette);
root->setAutoFillBackground(true);

QWidget* frame = new QFrame(root);
frame->setGeometry(QRect(20, 20, 400, 400));

QPalette palette1;
QBrush brush1( Qt::black );
palette1.setBrush(QPalette::Inactive, QPalette::Window, brush1);
palette1.setBrush(QPalette::Active, QPalette::Window, brush1);
frame->setPalette(palette1);
frame->setAutoFillBackground(true);

QWidget* groupBox = new QGroupBox(frame);
groupBox->setGeometry(QRect(5, 5, 200, 400 ));

QPalette palette2;
QBrush brush2( Qt::red );
palette2.setBrush(QPalette::Inactive, QPalette::Window, brush2);
palette2.setBrush(QPalette::Active, QPalette::Window, brush2);
groupBox->setPalette(palette2);
groupBox->setAutoFillBackground(true);

root->setWindowTitle( "testWidgetRect" );
root->show();
}

static void testGraphicsRect()
{
// test graphics
QGraphicsScene* scene = new QGraphicsScene;
scene->setSceneRect( 0, 0, 500, 500 );

QGraphicsRectItem* outerRect = new QGraphicsRectItem;
outerRect->setRect( QRectF( 20, 20, 400, 400 ) );
outerRect->setBrush( QBrush( Qt::black ) );

QGraphicsRectItem* innerRect = new QGraphicsRectItem;
innerRect->setRect( QRectF( 5, 5, 200, 400 ) );
innerRect->setParentItem( outerRect );
innerRect->setPen( QPen( Qt::red ) );
innerRect->setBrush( QBrush( Qt::red ) );

scene->addItem( outerRect );

QGraphicsView* view = new QGraphicsView(scene);

view->setWindowTitle( "testGraphicsRect" );
view->show();

}

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

// test widget
testWidgetRect();

// test graphics
testGraphicsRect();

return app.exec();

}

wysota
28th January 2009, 16:57
Widgets and graphics items have different ways of using the geometry so it is natural you will get different results when setting the same values. Widgets always have a rectangle of (0,0,width(), height()) whereas graphics items do not. The origin of the coordinate system of the black graphics item lies outside of the item itself thus the red item is positioned to the left and up of its parents top left corner.

lni
28th January 2009, 17:39
Widgets and graphics items have different ways of using the geometry so it is natural you will get different results when setting the same values. Widgets always have a rectangle of (0,0,width(), height()) whereas graphics items do not. The origin of the coordinate system of the black graphics item lies outside of the item itself thus the red item is positioned to the left and up of its parents top left corner.

I thought graphics items always have a boundingRect(), which must be implemented by derived class? That boundingRect should be treated no difference from widget rect...Not to mention my example used a rectangle item...

Parent item is child item's container, therefore the child can't go outside its parent boundary, and here is the document in "The Graphics View Framework":



Child coordinates are relative to the parent's coordinates. If the child is untransformed, the difference between a child coordinate and a parent coordinate is the same as the distance between the items in parent coordinates. For example: If an untransformed child item is positioned precisely in its parent's center point, then the two items' coordinate systems will be identical. If the child's position is (10, 0), however, the child's (0, 10) point will correspond to its parent's (10, 10) point.

Because items' position and transformation are relative to the parent, child items' coordinates are unaffected by the parent's transformation, although the parent's transformation implicitly transforms the child. In the above example, even if the parent is rotated and scaled, the child's (0, 10) point will still correspond to the parent's (10, 10) point. Relative to the scene, however, the child will follow the parent's transformation and position. If the parent is scaled (2x, 2x), the child's position will be at scene coordinate (20, 0), and its (10, 0) point will correspond to the point (40, 0) on the scene.

lni
28th January 2009, 18:12
The reason is that with current implementation, I have to worry about and keep track of all parent items' location related to the scene, it becomes nightmare...

In QWidget, we never have to worry about where is the parent's location, we don't care, because we know the child position is only related to its parent...

But not in QGraphicsItem, becasue all item's cordinates have the same origin, which is the scene's origin...this leaves users to manage complex QTransform all the way to the root...

wysota
28th January 2009, 18:25
In QWidget, we never have to worry about where is the parent's location, we don't care, because we know the child position is only related to its parent...
This is exactly the case with graphics items as well.


But not in QGraphicsItem, becasue all item's cordinates have the same origin, which is the scene's origin...

No, this is not true. You are simply using the item in a wrong way. QGraphicsRectItem::setRect() is not equivalent to QWidget::setGeometry()!

Substitute your testGraphicsRect() with this one:

static void testGraphicsRect()
{
// test graphics
QGraphicsScene* scene = new QGraphicsScene;
scene->setSceneRect( 0, 0, 500, 500 );

QGraphicsRectItem* outerRect = new QGraphicsRectItem;
outerRect->setRect( QRectF( 0, 0, 400, 400 ) );
outerRect->setBrush( QBrush( Qt::black ) );

QGraphicsRectItem* innerRect = new QGraphicsRectItem;
innerRect->setRect( QRectF( 0, 0, 200, 400 ) );
innerRect->setParentItem( outerRect );
innerRect->setPos(5,5);
innerRect->setPen( QPen( Qt::red ) );
innerRect->setBrush( QBrush( Qt::red ) );

scene->addItem( outerRect );
outerRect->setPos(20,20);
QGraphicsView* view = new QGraphicsView(scene);

view->setWindowTitle( "testGraphicsRect" );
view->show();

}

lni
28th January 2009, 19:39
You are simply using the item in a wrong way. QGraphicsRectItem::setRect() is not equivalent to QWidget::setGeometry()!


Maybe the document for QGraphicsRectItem::setRect() should be more clear, I did think it was the same as setGeometry and use it as setGeometry, and now I have to toss my codes...

Another question about setPos and translate: it appears that both functions have exactly the same visual effect. I did scale and rotate after setPos and translate, both produce identical view. Can you give example where they will produce different result after some operations? I will be using a lot setPos and/or translate, I hope you can give more detailed info on them so I don't make mistake again...

Also, what is the chance to have QGraphicsAbstractBuilder and QGraphicsBuilder, just as QFormBuilder? Serializing QGraphicsScene and its content is very useful...

Many thanks

wysota
28th January 2009, 22:35
Maybe the document for QGraphicsRectItem::setRect() should be more clear, I did think it was the same as setGeometry and use it as setGeometry, and now I have to toss my codes...

Please don't blame the docs, they are very clear about this, it's just you didn't read them carefully enough.

The Graphics View coordinate system.


Another question about setPos and translate: it appears that both functions have exactly the same visual effect.
Yes, that's correct.

I did scale and rotate after setPos and translate, both produce identical view. Can you give example where they will produce different result after some operations?
QGraphicsItem::pos() will probably return a different value which can be very important in many situations.


I will be using a lot setPos and/or translate, I hope you can give more detailed info on them so I don't make mistake again...
Use setPos() and forget about translate(), it's purely for completeness.


Also, what is the chance to have QGraphicsAbstractBuilder and QGraphicsBuilder, just as QFormBuilder? Serializing QGraphicsScene and its content is very useful...
None, I guess. There is no data format for storing graphics items. The easiest way to do serialization is to use QDataStream.

lni
28th January 2009, 22:58
Please don't blame the docs, they are very clear about this, it's just you didn't read them carefully enough.

Yes, it is my careless. People tend to skip most part of document. :)


Use setPos() and forget about translate(), it's purely for completeness.


setPos and translate are very very different and produce totally different results after several scale and translate operations. It took me 4 hours to finally find out that I should be using setPos!


You are very helpful! Thank you!