Child items not movable in QGraphicsView when ItemIsMovable flag is set

c++, qgraphicsitem, qt, qt5, qtgui

Solution

You say that your child items aren't getting mouse events, but they are. You can verify this by adding a `void mousePressEvent(QGraphicsSceneMouseEvent * event)` to Vertice and noticing that it is getting called.

Your problem is that Qt is ignoring the `ItemIsMovable` flag on a child QGraphicsItem. It's not even setting the flag when you ask.

You can verify this by changing your Vertice constructor:

Vertice::Vertice(QGraphicsItem * parent) :
    QGraphicsItem(parent)
{
    setFlag(ItemIsMovable);
    Q_ASSERT(flags() & ItemIsMovable); // fails
    setFlag(ItemIsSelectable);
    setFlag(ItemSendsGeometryChanges);
}

Now why would that be? As the programming Jedi say: "use the source, Luke!"

https://qt.gitorious.org/qt/qtbase/source/7df3321f934e5bd618e2ad00bf801f2b7edd31df:src/widgets/graphicsview/qgraphicsitem.cpp#L1789

Notice that one of the things it does when flags are set is it offers that to `itemChange` to inspect with an `ItemFlagsChange` notification. Not only that, but it allows the flags to be overwritten by the result of that call. But look at your implementation of itemChange() on Vertice:

QVariant Vertice::itemChange(GraphicsItemChange change, const QVariant & value)
{
    switch(change) {
        case QGraphicsItem::ItemSelectedHasChanged:
            qWarning() << "vertex: " + value.toString(); // never happened
            break;
        default:
            break;
    }
}

Uh-oh. No return result! Add this line to the end, as you have in your Item:

return QGraphicsItem::itemChange(change, value);

...and there you have it. Other notes:

Singular of "Vertices" is actually "Vertex"

If you have a case like this, think about paring it down from whatever specific program you are writing. If you can demonstrate the problem with one child item and one parent item, then why have a loop making four? If selection isn't part of the problem--and code for hiding and showing vertices need not be involved--then why involve it? It would be much better to use the code you provide to give required virtual methods like `boundingRect()` rather than make others write it to test. See Short, Self-Contained, Compilable Example

Qt source is fairly readable and well-organized, so do get into the habit of looking at it...!

Problem

To resize items in a QGraphicsView, I'm putting child items representing vertices onto the item to be moved (using the parent-child relationship established in the constructor). Those are the four blue circles in the picture below: But the child vertices are not receiving mouse events. Only the parent item (red square) is getting mouse events. Here is the definition for `Item`: ``` Item::Item(QGraphicsItem * parent) : QGraphicsItem(parent) { setFlag(ItemIsMovable); setFlag(ItemIsSelectable); setFlag(ItemSendsGeometryChanges); setCacheMode(DeviceCoordinateCache); } void Item::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->fillRect(option->rect,Qt::red); } QVariant Item::itemChange(GraphicsItemChange change, const QVariant & value) { switch(change) { case QGraphicsItem::ItemSelectedHasChanged: qWarning() << "item: " + value.toString(); updateVertices(value.toBool()); break; default: break; } return QGraphicsItem::itemChange(change, value); } void Item::updateVertices(bool visible) { if(visible) { if(vertices.length() == 0) { for(int i = 0; i < 4; i++) vertices.append(new Vertice(this)); } else for(int i = 0; i < 4; i++) vertices[i]->setVisible(true); QRectF rect = boundingRect(); vertices[0]->setPos(rect.topLeft()); vertices[1]->setPos(rect.topRight()); vertices[2]->setPos(rect.bottomLeft()); vertices[3]->setPos(rect.bottomRight()); } else { for(int i = 0; i < 4; i++) { p_vertices[i]->setVisible(false); } } } ``` While here is the definition for `Vertice`: ``` Vertice::Vertice(QGraphicsItem * parent) : QGraphicsItem(parent) { setFlag(ItemIsMovable); setFlag(ItemIsSelectable); setFlag(ItemSendsGeometryChanges); } void Vertice::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->setBrush(Qt::blue); painter->setPen(Qt::darkGray); painter->drawEllipse(-5,-5,10,10); } QVariant Vertice::itemChange(GraphicsItemChange change, const QVariant & value) { switch(change) { case QGraphicsItem::ItemSelectedHasChanged: qWarning() << "vertex: " + value.toString(); // never happened break; default: break; } } ```

Original source