问题描述 解决方案 1. 自定义GraphicsView类 2. 记录鼠标位置 3. 图像缩放
问题描述 使用Qt的QGraphicsView
显示图像时,鼠标滚轮的动作由其自带的wheelEvent
事件控制。滚动滚轮可以控制图像上下移动。
现希望通过滚动滚轮实现图像缩放,且缩放的中心为鼠标所在位置。
解决方案 1. 自定义GraphicsView
类 HighGraphicsView
类继承自QGraphicsView
类,重写了wheelEvent
和mouseMoveEvent
两个事件。其中,对mouseMoveEvent
,在原有的基础上加入了触发鼠标位置变化的事件,这个鼠标位置是相对于View
的(x,y)
坐标。对wheelEvent
,放弃原有事件,记录滚动间隔,发送信号。
HighGraphicsView.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #pragma once #include <qgraphicsview.h> #include <QMouseEvent> #include <QWheelEvent> #include <QScrollBar> class HighGraphicsView : public QGraphicsView { Q_OBJECT public : HighGraphicsView (QWidget *parent = nullptr ); ~HighGraphicsView (); signals: void mousePositionChanged (int x, int y) ; void wheelScrollChanged (int step) ; protected : void wheelEvent (QWheelEvent *event) ; void mouseMoveEvent (QMouseEvent *event) ; private : int x, y; };
HighGraphicsView.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include "HighGraphicsView.h" HighGraphicsView::HighGraphicsView (QWidget *parent) :QGraphicsView (parent) { } HighGraphicsView::~HighGraphicsView () { } void HighGraphicsView::wheelEvent (QWheelEvent *event) { QPoint numPixels, numDegrees; numPixels = event->pixelDelta (); numDegrees = event->angleDelta () / 8 ; int step = 0 ; if (!numPixels.isNull ()) { step = numPixels.y (); } else if (!numDegrees.isNull ()) { QPoint numSteps = numDegrees / 15 ; step = numSteps.y (); } emit wheelScrollChanged (step) ; } void HighGraphicsView::mouseMoveEvent (QMouseEvent *event) { QGraphicsView::mouseMoveEvent (event); x = event->x (); y = event->y (); emit mousePositionChanged (x, y) ; }
2. 记录鼠标位置 鼠标的位置包括三类:
图像上的位置;
View
中的位置;
Scene
中的位置;
其对应的转换关系如下所示:
其中,是View->horizontalScrollBar()->value()
的值,是View->verticalScrollBar()->value()
的值,是当前图像方向上的缩放比例,其表达式为:
在引用HighGraphicsView
的类中,存储两类鼠标位置:
1 2 3 int x, y; int viewX, viewY;
创建槽onMousePositionChanged
,用来接收View
发出的mousePositionChanged
信号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void onMousePositionChanged (int x, int y) { viewX = x; viewY = y; QRectF rect = View->mapToScene (View->viewport ()->geometry ()).boundingRect (); x += int (rect.x () - 1 ); y += int (rect.y () - 1 ); x = int (double (x) * (double (Width) / double (currentWidth))); y = int (double (y) * (double (Height) / double (currentHeight))); this ->x = x; this ->y = y; if (x >= Width || x < 0 || y >= Height || y < 0 ) { ui.statusBar->showMessage (u8"Outside" ); } else { ui.statusBar->showMessage (QString ().sprintf (u8"(x=%d, y=%d)" , x, y)); } }
绑定信号与槽
1 2 connect (View, SIGNAL (mousePositionChanged (int , int )), this , SLOT (onMousePositionChanged (int , int )));
3. 图像缩放 要以鼠标为中心进行图像缩放,可以分为两个步骤进行:1. 缩放;2. 平移。缩放过程较为简单,只需要根据比例对原图进行缩放即可。平移的目的是为了使图像在缩放前后鼠标所在的位置相对于图像不变、相对于视窗(View
)不变,不变量为、。
根据之间的关系(见第1小节),可以得到
由于 、 是不变量,因此可以得到变化量 的值,即View
相对于Scene
的偏移量:
通过View->horizontalScrollBar()->setValue()
和View->verticalScrollBar()->setValue()
设置偏移量,完成平移。
创建响应滚轮变化信号的槽:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void onWheelScrollChanged (int step) { currentHeight += currentHeight / 20 * step; currentWidth += currentWidth / 20 * step; ImageItem->setPixmap (QPixmap::fromImage (Image.scaled (currentWidth, currentHeight))); Scene = new QGraphicsScene (this ); Scene->addItem (ImageItem); View->setScene (Scene); int horizontal, vertical; horizontal = int (double (x * currentWidth) / double (Width) - viewX); vertical = int (double (y * currentHeight) / double (Height) - viewY); View->horizontalScrollBar ()->setValue (horizontal); View->verticalScrollBar ()->setValue (vertical); }
绑定信号与槽
1 2 connect (View, SIGNAL (wheelScrollChanged (int )), this , SLOT (onWheelScrollChanged (int )));