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
.
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
.