Reputation: 14615
This question may or may not relate to QGraphicsItemGroup
- I have never seen this behavior before though....
Briefly: I am deselecting an item, yet the action doesn't take place unless I call the read-only scene().selectedItems()
- even if I don't use it.
Details:
I have a custom QGraphicsScene
class, that has to perform a lot of operations on the selectedItems()
.
If items are in a group, they should not be used in any operations - only the group should be used.
So I create my addToGroup()
method to perform what I need:
void addToGroup(QList<QGraphicsItem *> children) {
foreach(QGraphicsItem* child, children)
{
child->setSelected(false);
QGraphicsItemGroup::addToGroup(child);
}
}
Unfortunately, the items refuse to get deselected !
And then, I added debug messages after each line - and found that adding debug messages changes the outcome !
void addToGroup(QList<QGraphicsItem *> children) {
foreach(QGraphicsItem* child, children)
{
child->setSelected(false);
scene()->selectedItems(); // this makes it work !
QGraphicsItemGroup::addToGroup(child);
}
}
Calling scene()->selectedItems();
- which should be read--only - makes the items actually get deselected !
Please allow me to make sense of this !
Full sample code:
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QDebug>
class Item {
public:
Item(int id) {
m_id = id;
}
private:
int m_id;
};
class RectItem: public QGraphicsRectItem, public Item
{
public:
RectItem(int id) : Item(id) {
setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
setRect(QRectF(0,0,100,100));
setPos(QPointF(10+110*id,10));
setSelected(true);
}
};
class GroupItem: public QGraphicsItemGroup, public Item
{
public:
GroupItem(int id) : Item(id) {
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsSelectable);
}
void addToGroup(QList<QGraphicsItem *> children) {
foreach(QGraphicsItem* child, children)
{
child->setSelected(false);
//scene()->selectedItems().size();
QGraphicsItemGroup::addToGroup(child);
}
}
};
class MyScene: public QGraphicsScene
{
public:
MyScene() {}
void group() {
GroupItem* g = new GroupItem(items().size());
addItem(g);
g->addToGroup(selectedItems());
g->setSelected(true);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyScene s;
QGraphicsView view(&s);
s.setSceneRect(0, 0, 230, 120);
view.show();
RectItem* r0 = new RectItem(0);
s.addItem(r0);
RectItem* r1 = new RectItem(1);
s.addItem(r1);
s.group();
qDebug() << "Should only have 1 (group) selected out of 3\nTotal items:" << s.items().size() << "; Selected items:" << s.selectedItems().size();
return app.exec();
}
Upvotes: 3
Views: 2297
Reputation: 6776
Good observation regarding scene().selectedItems()
. There is actually one more inconsistency with QGraphicsItem
item selected state when such item is added to QGraphicsItemGroup
.
It appears that the internal private set selectedItems
accumulates items when a QGraphicsItem
is selected. However, if such item is unselected it is not removed from that set.
QGraphicsItem
is removed from that set only when the function QGraphicsScene::selectedItems()
is called and the member function QGraphicsItem::isSelected()
of that item returns false
.
In your case at first the items are added to the private scene selected set. Then the selected flag of those items is changed and the group object is added to that scene in selected state. So, there are three objects in the set. The trick is that now isSelected()
is true
for all items, since isSelected()
for items in the group always returns the same result as the group isSelected()
independently of items selected state. Thus, all three items appear in the list selectedItems()
.
If scene().selectedItems()
is called after child->setSelected(false)
the scene selected set is recreated and the child is removed. That gives final result with only one items in selectedItems()
.
The function QGraphicsScene::selectedItems()
is quite heavy since it is always generates new private set. It is bad to call it inside foreach
loop just to remove child items. The same goal can be achieved also by changing order of calls in your group()
function:
void group() {
GroupItem* g = new GroupItem(items().size());
// at first add items to group
g->addToGroup(selectedItems());
// add group to scene
addItem(g);
g->setSelected(true);
}
That gives only one item in selectedItems()
. It happens because when child items are added to the group that is not in the scene those items are removed from the scene. So, they are also removed from the selected set. Then three unselected items are added to the scene, but g->setSelected(true)
adds only group to the selected set.
However, if in the above example you call addItem(g)
after setSelected(true)
it will give again all three items in selectedItems()
:
void group() {
GroupItem* g = new GroupItem(items().size());
g->addToGroup(selectedItems());
g->setSelected(true);
addItem(g);
}
That happens since addItem(g)
adds simultaneously three items to the scene. All three items have isSelected()
return value true
.
Another inconsistency is related to the internal item selected state. It appears that if an item is added to the group the state of that item is frozen in the state that was before adding to that group. It means that if you add selected item to the group it is always displayed as selected (with dashed bounding rectangle) even if the group is not selected. Such view cannot be changed by calling setSelected(false)
for that item, since now that function changes only selected state of the group. The item can be displayed as selected even if its isSelected()
returns false
.
It looks that both those inconsistencies are not intended by the design and it makes sense to report them on the Qt bug tracker.
Upvotes: 2