PDA

View Full Version : Scene coordinates of an item - held by QGraphicsScene or QGraphicsItem?



ajo
14th November 2012, 15:02
Hello, this is my first question.
I was wondering which (perhaps both, perhaps neither...?) object - the QGraphicsItem object or the QGraphicsScene object - holds the scene coordinates of an item on a scene?
Thanks.

wysota
14th November 2012, 15:42
Just guessing here but with high probability...

Directly neither. Indirectly, the scene. In fact there is an index in the scene that keeps information about items occupying particular parts of the scene. But this is reverse mapping of what you want (mapping coordinates to items, not items to coordinates). However each item keeps its own position relative to its parent coordinate space. Therefore the scene coordinate is calculated by recursively mapping coordinates to parent items until there is no parent item (and thus the scene is considered to be the parent coordinate space).

ajo
15th November 2012, 07:44
Thank you, wysota.

I have been pointed to a 'QPointF pos' member of the class qgraphicsitem_p.h by someone else. I did not know about these private implementation (pimpl) classes of Qt. I'll need to study up a little on what they are, but if QGraphicsItem is the same as QGraphicsItem_p, then I guess the item does hold the coordinates, as you also say in your answer: "each item keeps its own position relative to its parent coordinate space."
Yet, if the coordinates of the scene should change, it probably means that each item needs to be updated as well?

wysota
15th November 2012, 08:27
No, because moving a parent item does not change position (held by "pos") of a child item. It only changes the scene position which is (probably) calculated on the fly (and maybe cached but again that's just a guess). Each item is said to live in its own coordinate space, or to be exact in coordinate space of its parent item. Each item defines its coordinates range by defining boundingRect() that is expressed in coordinate space of the item's parent.

ajo
15th November 2012, 09:40
Thanks again, wysota.

I wish I could post my code, but it's a line here and a line there.

I have scene items (they don't have parents), which are placeholders and they are to be replaced with other items. The placeholders are created where one double-clicks with a mouse and that position is captured by the QGraphicsScene scene and kept throughout the whole thing, for correct placement of the new items.

I delete the placeholders and attempt to place the new items there, but they end up at (2x, 2y), and not (x, y) - so I believe that somehow the initial correct position (x, y) is set to (0, 0) somewhere - which it isn't really, in scene coordinates. This led me to wonder if it is the scene or the item that holds the coordinates; I think knowing it would help me to track down the problem.

The scene and the view are both fixed-size and the same size. The items should be exact in coordinate space of the scene and the scene coordinates do not change - so creating a new item at those same coordinates, should put it there and not moved away from (0, 0) by a factor of 2.

It seems like it should be a simple thing.

Thanks again, I'll just have to go through the code again (and possibly reorganize it as well).

wysota
15th November 2012, 10:30
I have scene items (they don't have parents), which are placeholders and they are to be replaced with other items. The placeholders are created where one double-clicks with a mouse and that position is captured by the QGraphicsScene scene and kept throughout the whole thing, for correct placement of the new items.
Maybe you shouldn't create them here then? What are they needed for? Do they display any content?


I delete the placeholders and attempt to place the new items there, but they end up at (2x, 2y), and not (x, y) - so I believe that somehow the initial correct position (x, y) is set to (0, 0) somewhere - which it isn't really, in scene coordinates. This led me to wonder if it is the scene or the item that holds the coordinates; I think knowing it would help me to track down the problem.
I'd need to see the actual code but even without it I can suggest some things. The scene position of an item is calculated based on two values. One is the bounding rect of the item, the other is the position of the item. The item is placed so that it's (0,0) coordinate (based on the bounding rect) is placed in coordinates expressed by the position of the item. Therefore say you want to place a rectangle 100x50px big so that its top left corner occupies the (25,10) point. This can be done in many ways, for example a possible combination of proper values is (left=0,top=0,width=100,height=50) for the bounding rect and (left=25,top=10) as the position. Another is (-50,-25,100,50) and (75,35). Yet another is (25,10,100,50) and (0,0). Do you get the picture?

ajo
15th November 2012, 11:28
They don't really display content - it is to affirm to the user that their double-click was 'taken' by the computer. A dialog then pops up and based on user input, the item is changed.
I know if you have some item and its bounding rect is too small, the sides are cut off, and the bounding rect is also used to grab it - if it is set to be moveable.
I might have made a mistake with the bounding rect... I'm going to see if I can get the relevant code together and will post it.
Thanks a lot for your help!

wysota
15th November 2012, 12:12
I know if you have some item and its bounding rect is too small, the sides are cut off, and the bounding rect is also used to grab it - if it is set to be moveable.
What you say are all side effects of a bounding rect. The main purpose of it is to determine where your item is and what area of the scene needs to be rerendered when the item changes.

ajo
15th November 2012, 13:02
Hello,

I've taken the relevant code out of my project and am also attaching a picture.
The lines on the picture are to assist visualization.
The inner ellipses are where I double-clicked and the letters should be there.

Thanks again!

The code:

// This is a Qt Widget project
// I use Qt Creator 2.4.1

// Process: user clicks scene - scene signal connected to Widget slot - Widget calls dialog and user makes a choice -
// choice is sent back to scene - scene creates a sceneObject object; classes below:

// Widget.h:
scene *someScene;
QGraphicsView *gView;

// Widget.cpp:
// Widget constructor:
// creation of scene and view (they are properly organized in a Layout
gView = new QGraphicsView(this);
gView->setFixedSize(400,400);
someScene = new scene();

connect(someScene,SIGNAL(sceneClicked(QPointF)),th is,SLOT(userClickedScene(QPointF)));

// Widget function:
void Widget::userClickedScene(const QPointF aPosition) {
infoDialog getInfo;
if (getInfo.exec() == QDialog::Accepted){
// this dialog asks the user to make a choice... A / B / C / D
someScene->userPicksType_ReplaceEllipse(userChoice, aPosition);
}

// scene.h:

class scene : public QGraphicsScene
{
Q_OBJECT

// scene.cpp:

// scene constructor:
setSceneRect(-200, -200, 400, 400);

// scene mouse-event function:
void scene::mouseDoubleClickEvent(QGraphicsSceneMouseEv ent *event) {
QPointF location = event->scenePos();
double xVal = location.x();
double yVal = location.y();
QColor color = QColor(qrand() % 256, qrand() % 256, qrand() % 256);
if (items(xVal,yVal,10,10).isEmpty()){
QGraphicsEllipseItem *el = addEllipse(xVal, yVal, 10, 10, color, QBrush(color));
emit sceneClicked(location);
}

// scene function:
void scene::userPicksType_ReplaceEllipse(const int userChoice, const QPointF location)
{
sceneObjects *sOb = new sceneObjects(0, this, userChoice, location);
objectList->append(qMakePair(location, sOb)); // made a list - part of my effort to figure it out.8417
sOb->setFlags(QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemIsMovable); // does not work

sOb->setPos(location);
update(sceneRect());
addEllipse(sOb->scenePos().x(), sOb->scenePos().y(), 5, 5); // to illustrate positioning
}

// sceneObjects.cpp: (subclassed from QGraphicsObject)
sceneObjects::sceneObjects(QGraphicsObject *parent, QGraphicsScene *scene,
const int userChoice, const QPointF whereOnScene)
: QGraphicsObject(parent), paintWhat(0), x(0), y(0)
{
paintWhat = userChoice;
x = whereOnScene.x();
y = whereOnScene.y();
scene->addItem(this);
}

void sceneObjects::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
switch(paintWhat)
{
case 0:
painter->drawText(x,y,"A");
setPos(x, y);
break;
case 1:
painter->drawText(x,y,"B");
setPos(x, y);
break;
case 2:
painter->drawText(x,y,"C");
setPos(x, y);
break;
case 3:
painter->drawText(x,y,"D");
setPos(x, y);
break;
}
}

QRectF sceneObjects::boundingRect() const{
return QRectF(0,0,10,10);
}

wysota
15th November 2012, 15:02
Calling setPos() in paint() certainly doesn't make any sense. The whole paint() method is invalid. You are supposed to paint within your boundingRect() (in your case within (0,0,10,10) rectangle), not in the position determined by pos(). You have missed the whole point of my earlier explanation... Your item is within your bounding rect.

ajo
15th November 2012, 15:13
Yes, I didn't get it before, but I think I'm starting to grasp it now and also: where I have to go and look to figure this out.
There is the scene with its scene rect and the item with its bounding rect. I've been trying to move the item around on the scene - when I should have been placing it within its own rect.
There is something important I'm missing about the interaction between an item bounding rect and the scene rect.

Thanks a lot for your help!