Current implementation, zooms towards the center of View so items present in the top left corner or the current mouse pointer is not visible when we Zoom it.
I want zoom functionality based on the current mouse pointer so items present on the current mouse pointer zoom towards the center of the view.
Code for Zoom base don center of view area
void csGuiView::wheelEvent(QWheelEvent *event)
{
if ((event->modifiers()&Qt::ControlModifier) == Qt::ControlModifier
&& event->angleDelta().x() == 0)
{
QPoint pos = event->pos();
QPointF posf = this->mapToScene(pos);
double angle = event->angleDelta().y();
double scalingFactor;
if(angle > 0)
{
scalingFactor = 1 + ( angle / 360 * 0.1);
}else if (angle < 0)
{
scalingFactor = 1 - ( -angle / 360 * 0.1);
} else
{
scalingFactor = 1;
}
m_pvtData->m_scale = scalingFactor;
this->scale(scalingFactor, scalingFactor);
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);
this->ensureVisible(QRectF(QPointF(lf, tf) - newPos + posf,
QSizeF(wf, hf)), 0, 0);
}
if ((event->modifiers()&Qt::ControlModifier) != Qt::ControlModifier) {
QGraphicsView::wheelEvent(event);
}
event->accept();
}
To zoom always centered at mouse pointer – the position of mouse pointer just has to become the origin of scaling.
It sounds that simple but I struggled a bit to prepare a demonstration. (I'm not that good in linear algebra, sorry.) However, I finally got it running.
My sample code
testQWidget-Zoom.cc
:and these three lines are the actual interesting ones (in
Canvas::wheelEvent()
):And this is how it looks:
The first image is a snapshot of the application just after starting it.
Then I pointed into the center of the red rectangle and turned the wheel slightly. The red rectangle grew around the mouse pointer as intended.
1st Update:
And, this is the updated version which uses screen coordinates directly (instead of converting everything to NDCs):
The relevant three lines didn't change much – the mouse coordinates are applied directly to transformation.
Btw. I changed the rendering – it now scales line width as well as I used
in
Canvas::paintEvent()
instead of transforming all points "manually".The snapshot shows the application after I pointed into the center of the blue rectangle and turned the mouse wheel:
2nd Update:
The suggested matrix manipulation works in a
QGraphicsView
as well:Using a
QGraphicsView
simplifies code as no rendering code is needed – it's already built-in.As I have not (yet) much experience with
QGraphicsView
, another issue hit me quite hard: TheQGraphicsView
is able to fix the view position automati[cg]ally after a transformation has been applied. In my case, this was rather counter-productive as obviously my transformation and theQGraphicsView
seemed to "pull" in opposite directions.Hence, I've learnt my lesson of the day:
QGrapicsView::setTransformationAnchor(QGraphicsView::NoAnchor)
is necessary to switch off this (in my case not-intended) auto-centering.The other detail I find worth to notice is
QGraphicsView::mapToScene()
which can be used to conveniently convert widget coordinates (e.g. mouse coordinates) to scene space.