Question

I have a QGraphicsView and a QGraphicsScene. Depending on user input some QGraphicsItem may be placed on the scene. This item is both selectable and movable.

When the scene is larger than the view scrollbars appear (they are set to show when necessary).

When item is moved by the user near the edge of the view the scene width/height are stretched accordingly - I am making the scene bigger.

The question is how to force the scrollbars to scroll with the scene when the item is near the border of the view? Feature I think common in any graphic editor. In the MouseMoveEvent of the scene I am making the scene bigger, force the sliders to move and update the visible rectangle accordingly.

This does not work as intended. Even thought the scrolls are adjusting to the new scene size there is no smooth movement in the view. Is there a better way in doing this?

Some explanations:

    itemUnderCursor = currently slected QGraphicsItem
    qgv = QGraphicsView

Code snippet:

    // check if item is near the border
    QPointF point = itemUnderCursor->mapToScene(itemUnderCursor->boundingRect().topLeft());
    double delta = 0;

    if(point.x() < visibleRect.left())
    {
        // prevent from drawing outside the scene
        itemUnderCursor->setPos(visibleRect.left(), itemUnderCursor->scenePos().y());

        if(event->scenePos().x() < oldMousePos.x()-3)
        {
            // stretch the scene
            if(qgv->horizontalScrollBar()->value() <= 0)
                setSceneRect(QRectF(QPointF(sceneRect().x() - 3, sceneRect().y()), sceneRect().bottomRight()));
            /*
             * disable signals from DrawingArea in order to avoid
             * recursive calls of mouseMoveEvent then enabling them
             * back to handle the rest of events
             */
            this->blockSignals(true);
            delta = point.x() - originalRect.left();
            qgv->horizontalScrollBar()->setValue(hScrollOriginalValue + delta);
        }
        oldMousePos = event->scenePos();
        this->blockSignals(false);

        // update the visible rectangle
        visibleRect = getVisibleRect(qgv);
    }

    if(point.x() + itemUnderCursor->boundingRect().width() > visibleRect.right())
    {
        // prevent from drawing outside the scene
        itemUnderCursor->setPos(visibleRect.right() - itemUnderCursor->boundingRect().width(), itemUnderCursor->scenePos().y());
        if(event->scenePos().x() > oldMousePos.x()+3)
        {
            // stretch the scene
            if(qgv->horizontalScrollBar()->value() >= 0)
                setSceneRect(QRectF(sceneRect().topLeft(), QPointF(sceneRect().bottomRight().x() + 3, sceneRect().bottomRight().y())));

            /*
             * disable signals from DrawingArea in order to avoid
             * recursive calls of mouseMoveEvent then enabling them
             * back to handle the rest of events
             */
           delta = point.x() + itemUnderCursor->boundingRect().width() - originalRect.right();
           this->blockSignals(true);
           qgv->horizontalScrollBar()->setValue(hScrollOriginalValue + delta);
        }
        oldMousePos = event->scenePos();
        this->blockSignals(false);

        // update the visible rectangle
        visibleRect = getVisibleRect(qgv);
    }

I am doing the same for the top and bottom border of the QGraphicsView.

Was it helpful?

Solution

It appears that my previous attempt was terribly complicated while the solution is in fact very easy.

Instead of the previous code it was enough to write:

qgv->ensureVisible(itemUnderCursor);

and make sure that sceneRect() will not be set with any value, but rather left to be handled by the scene itself.

This allowed the scene to automatically adjust its size accordingly to the items on it and forced the scrollbars to follow the moving item when outside the visible rectangle of QGraphicsView.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top