1
votes

I have a GraphicsBoxItem that holds a list of GraphicsImageItem (some kind of floating buttons around the box). I create them on the fly in focusInEvent() and destroy them in focusOutEvent(). Some of the GraphicsImageItem should move with the parent when clicked, some of them should stay at the same spot when clicked, until the box gets a focusOut by clicking outside of any graphics items. Is there a way to prevent a child QGraphicsItem from being moved with the parent?

class GraphicsBoxItem : public QObject, public QGraphicsItem
{
    Q_OBJECT
private:
    QColor mColor;
    QVector<GraphicsImageItem*> mItemList;
public:
    GraphicsBoxItem(QGraphicsItem *parent = NULL)
        :QGraphicsItem(parent)
    {
        mColor = Qt::lightGray;
        setFlag(QGraphicsItem::ItemIsSelectable);
        setFlag(QGraphicsItem::ItemIsFocusable);
    }

    QRectF boundingRect() const { return QRectF(50, 20, 100, 60); }

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        painter->setBrush(mColor);
        painter->drawRect(boundingRect());
    }
    virtual void focusOutEvent(QFocusEvent * event)
    {
        foreach(GraphicsImageItem *item1, mItemList)
        {
            item1->deleteLater();
            item1 = NULL;
        }
        mItemList.clear();
        mColor = Qt::lightGray;
        QGraphicsItem::focusOutEvent(event);
    }

    virtual void focusInEvent(QFocusEvent * event)
    {
        GraphicsImageItem *movableItem = new GraphicsImageItem(this);
        movableItem->setPos(150, 20);
        mItemList.push_back(movableItem);
        movableItem->installSceneEventFilter(this);
        connect(movableItem, SIGNAL(SignalClicked()), this, SLOT(SlotButtonClicked()));

        GraphicsImageItem *nonMovableItem = new GraphicsImageItem(this);
        nonMovableItem->setPos(20, 20);
        mItemList.push_back(nonMovableItem);
        nonMovableItem->installSceneEventFilter(this);
        connect(nonMovableItem, SIGNAL(SignalClicked()), this, SLOT(SlotButtonClicked()));

        mColor = Qt::blue;
        QGraphicsItem::focusInEvent(event);
    }

    bool sceneEventFilter(QGraphicsItem* target, QEvent* event)
    {
        if(event->type() == QEvent::GraphicsSceneMousePress || event->type() == QEvent::GraphicsSceneMouseDoubleClick)
        {
            GraphicsImageItem* item = dynamic_cast<GraphicsImageItem*>(target);
            if(item)
            {
                item->SignalClicked();
                qDebug() << "image button was clicked: Need to set the focus back to the box";
                setFocus();
                return true;
            }
        }
        return QGraphicsItem::sceneEventFilter(target, event);
    }
public slots:

    void SlotButtonClicked()
    {
        setPos(pos().x() + 10, pos().y()+10);
    }
};

SignalClicked() moves the GraphicsBoxItem by 10 pixels. Some of the GraphicsImageItem stay put, some move with GraphicsBoxItem:

class GraphicsImageItem : public QGraphicsSvgItem
{
    Q_OBJECT
public:
    GraphicsImageItem::GraphicsImageItem(QGraphicsItem *parent = NULL)
    : QGraphicsSvgItem(QString(":/images/icon.svg"), parent)
    {
        setFlag(QGraphicsItem::ItemIsSelectable);
    }

    QRectF boundingRect() const { return QRectF(0, 0, 30, 30); }

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        painter->setBrush(Qt::lightGray);
        painter->drawRect(boundingRect());
        renderer()->render(painter, boundingRect().adjusted(3, 3, -3, -3));
    }

Q_SIGNALS:
    void SignalClicked();
};
3

3 Answers

4
votes

A child's position is always relative to its parent, if the parent moves, then the child's absolute position changes too.

If you want to create the illusion that this doesn't happen, you will have to manually move the child in the opposite direction, so it can remain in the same absolute position.

The other option would be to not have the items as children in the first place.

Maybe you should create an invisible item, then parent the movable item as well as the non-movable children to it, and the movable children - under the movable item. Then you simply only move the movable item - its children move, its siblings stay in the same position, as their parent doesn't move.

1
votes

For the child items you don't want to move, try set this flag:

setFlag(QGraphicsItem::ItemIgnoresTransformations);
0
votes

If you can subclass your child items, then you can override the itemChange() method and watch for:

ItemScenePositionHasChanged

The item's scene position has changed. This notification is sent if the ItemSendsScenePositionChanges flag is enabled, and after the item's scene position has changed (i.e., the position or transformation of the item itself or the position or transformation of any ancestor has changed). The value argument is the new scene position (the same as scenePos()), and QGraphicsItem ignores the return value for this notification (i.e., a read-only notification).

Then then return the child item's last scenePos() which you'll have to store as a member and update each scene position change.

Remember to set the flag:

ItemSendsGeometryChanges

in your child subclass's ctor.