PDA

View Full Version : QGraphicsItem geometry change implementation



Narada
11th April 2016, 18:16
I am trying to figure out how objects in a QGraphics Scene/View can be manipulated and redrawn.

From what I have learnt so far, when a QGraphicsItem needs to be added to the scene,

1. The object is derived from QGraphicsItem.
2. boundingRect(), and paint() needs to be reimplemented.
3. If some of the sizes need to be changed, use prepareGeometry(), and update().

So, I am working with two actions. One to add a box to the scene, and the other to increase the size when selected.

I tried to get it to work in one application and it did not work.(diagram.zip). The sizes of the item seem to reset before painting. I can see this if I keep increasing the size and trap the code. However, under certain paths it seem to work. For example, if I add a child to a box and select the parent, the size of the parent can be changed programmatically.

However, I created the minimum code required and it worked.
The code is in GrScene.zip

Why is there a difference in behavior?

Thanks in advance.

d_stranz
11th April 2016, 20:16
In this code:



void DiagramWindow::increaseSize()
{

QList<QGraphicsItem *> items = scene->selectedItems();
if (items.count() == 1) {
Node *selNode = dynamic_cast<Node *>(items.first());
selNode->increaseSize();
}
return;
}

What happens if the selected node is a child of another node? I think in that case, both nodes will be marked as selected so your count will be 2, not 1. Have you looked at the size of the count in the cases where "works" and "doesn't work"?

Narada
11th April 2016, 20:50
The count does not seem to matter. However, the path it gets there to increase the size seems to.

MainWindow::increaseSize() --> Node::increaseSize() - does not work. Actually it resets the size. No idea how that happens
MainWindow::selectedNode() --> Node::increaseSize() - seems to work.


If I go through the second path and try the first path, the sizes get reset.

If I try to trace the code, it goes into the moc_ code. I am wondering if some of the variables are initialized here. I cannot trace the code all the way.

d_stranz
11th April 2016, 21:14
The count does not seem to matter.

Of course the count matters. Your code (in either example) will do nothing if the selected node count is not exactly 1.

In your DiagramWindow:: selectedNode() code, you -are- resetting the node size to nodeh x nodew when the node has no children. And in addChild(), since you call this method -before- you add a child to it, you are resetting the size there as well.

I'm not sure why you wrote selectedNode() that way, but it looks like some confused thinking about what should happen when you add a child node.

Narada
11th April 2016, 22:54
Hi d_stranz,

You are correct in that the size matters, and how the reset happens. Great catch.


I'm not sure why you wrote selectedNode() that way, but it looks like some confused thinking about what should happen when you add a child node.

Originally, the selectedNode() was used to return when a node is selected.

I added code here to try out the code for increasing the size of the widget on selecting a node. So, this shows that I can actually increase the size of the widget. Then I went ahead and implemented a new QAction for that. Somehow using the same code the new path does not work.

I was expecting the new QAction will do the same thing. The new path follows these two functions. Here the count is used only to isolate one selected item and the dynamic_cast is used to ensure it is of the correct type in the original example.

For the values not to be reset, I commented the code in selectedNode().

So, this is the path that does not work,


void DiagramWindow::increaseSize()
{

QList<QGraphicsItem *> items = scene->selectedItems();
if (items.count() == 1) {
Node *selNode = dynamic_cast<Node *>(items.first());
selNode->increaseSize();
}
return;
}

void Node::increaseSize(void)
{
prepareGeometryChange();
baseh = baseh + nodeh;
basew = basew + nodew;
update();
return;
}

and this is the path that works in the second, isolated example.


void MainWindow::on_actionIncrease_Size_triggered()
{
QList<QGraphicsItem *> items = scene->selectedItems();
if (items.count() == 1) {
Node *selNode = dynamic_cast<Node *>(items.first());
selNode->increaseSize();
}
return;
}
void Node::increaseSize()
{
prepareGeometryChange();
baseh += nodeh;
basew += nodew;
update();
}

Added after 40 minutes:

So, I thought the reset problem was over. It ain't.

The new selectedNode() function does what the original code intended to do, simply returns a pointer to the selected node.


Node *DiagramWindow::selectedNode() const
{

QList<QGraphicsItem *> items = scene->selectedItems();
if (items.count() == 1) {
Node *dad = dynamic_cast<Node *>(items.first());
return dad;
} else
return 0;
}

If I break at Node::increaseSize(), the baseh, basew do increase, but somewhere they get reset, and in the next cycle, the values are the original values.

d_stranz
12th April 2016, 04:14
Here the count is used only to isolate one selected item

And if for any reason at all, the count is NOT 1, nothing will be executed. Do us both a favor and put a

qDebug() << "items.count() = " << items.count();
line in there after you retrieve the selected items, just to prove to both of us whether the count is anything other than one. You can't claim the code doesn't work when you don't demonstrate that the if() clause is ever executed.

It also isn't clear how you get to DiagramWindow:: increaseSize() in the first place. Is this a slot? What signal is it connected to? Do you have any evidence that the method is ever called?


the dynamic_cast is used to ensure it is of the correct type in the original example.

Yes, but you should also realize that dynamic_cast<> returns a NULL pointer if the type is not correct, so your code as it stands now is guaranteed to blow up since you don't check for NULL but just go ahead and use the pointer no matter what.

--Edit--

Ummm, I just took a bit closer look at the DiagramWindow.cpp file you posted earlier. See if you can spot anything suspicious about this code:



void DiagramWindow::createActions()
{
// ... stuff omitted

increaseSizeAction = new QAction(tr("Increase"),this);
increaseSizeAction->setIcon(QIcon(":/images/increaseSize.png"));
connect(increaseSizeAction, SIGNAL(triggered(bool)),
this, SLOT(increaseSize()));


decreaseSizeAction = new QAction(tr("Decrease"),this);
decreaseSizeAction->setIcon(QIcon(":/images/decreaseSize.png"));
connect(increaseSizeAction, SIGNAL(triggered(bool)),
this, SLOT(decreaseSize()));

normalSizeAction = new QAction(tr("Normal"),this);
normalSizeAction->setIcon(QIcon(":/images/normalSize.png"));
connect(increaseSizeAction, SIGNAL(triggered(bool)),
this, SLOT(normalSize()));
}

Narada
12th April 2016, 14:21
my action has a copy/paste error!!!

I should stick to my daytime job.

Added after 8 minutes:

The interesting thing is how I convinced myself on the proper connection to the slots. I put a break point in my destination function, node::increaseSize() and thought if I get there I set it up correct. Wrong!!!

d_stranz
12th April 2016, 15:15
And your copy/paste error completely explains what you were seeing. Slots are executed in the order that connections are made, so the increaseSizeAction first increased the size, then decreased the size, then reset it...

It is a good practice to check the call stack when you are puzzled about problems like this. You would have seen this series of calls and found the answer much more quickly.