Zooming into and out of a QGraphicsView so that the position under the mouse cursor remains fixed is easy, right? Sadly, it isn't. I leave my best working approach here for you to enjoy and hopefully save you some time.
Before coming up with this, I browsed the interwebs and tried a few other things. I always ended up with not-working code or a slight drift while repeatedly zooming.
void GraphicsView::wheelEvent(QWheelEvent* e) {
if ((e->modifiers()&Qt::ControlModifier) == Qt::ControlModifier
&& e->angleDelta().x() == 0) {
QPoint pos = e->pos();
QPointF posf = this->mapToScene(pos);
double by;
double angle = e->angleDelta().y();
if (angle > 0) { by = 1 + ( angle / 360 * 0.1); }
else if (angle < 0) { by = 1 - (-angle / 360 * 0.1); }
else { by = 1; }
this->scale(by, by);
double w = this->viewport()->width();
double h = this->viewport()->height();
double wf = this->mapToScene(QPoint(w-1, 0)).x()
- this->mapToScene(QPoint(0,0)).x();
double hf = this->mapToScene(QPoint(0, h-1)).y()
- this->mapToScene(QPoint(0,0)).y();
double lf = posf.x() - pos.x() * wf / w;
double tf = posf.y() - pos.y() * hf / h;
/* try to set viewport properly */
this->ensureVisible(lf, tf, wf, hf, 0, 0);
QPointF newPos = this->mapToScene(pos);
/* readjust according to the still remaining offset/drift
* I don't know how to do this any other way */
this->ensureVisible(QRectF(QPointF(lf, tf) - newPos + posf,
QSizeF(wf, hf)), 0, 0);
e->accept();
}
if ((e->modifiers()&Qt::ControlModifier) != Qt::ControlModifier) {
/* no scrolling while control is held */
QGraphicsView::wheelEvent(e);
}
}
Any Tipps to make this shorter/better are very much appreciated.
thanks a lot
ReplyDeletehave you tried setTransformationAnchor(QGraphicsView::AnchorUnderMouse) ?
ReplyDelete:> Thx
ReplyDeleteThanks for this article! I was having exactly the same issue. Interestingly, the code to ensureVisible() had no effect. What worked for me is in GraphicsView::wheelEvent(): 1) call setTransformationAnchor(QGraphicsView::AnchorUnderMouse), 2) call e->accept() and DO NOT call QGraphicsView::wheelEvent(), like your code does. Not doing #2 caused the view to pan undesirably, causing the same point to drift from under the cursor so the next wheel event centered a different point.
ReplyDeleteThis was so much helpful. Thanks alot
ReplyDelete