QGraphicsView缩放内容时保持鼠标悬停位置不变
有时在QGraphicsView显示一张图片时,我们需要缩放图像同时保持鼠标悬停位置内容的位置不变。这时候就需要我们在缩放时实时控制QGraphicsView的水平和垂直滚动条控件的位置。本文给出一个实现此功能的简单例子。此例子在VS2017和Qt5.9的环境下测试通过。软件效果如下:
头文件:
class MImageView : public QGraphicsView { Q_OBJECT public: MImageView(QWidget* parent = 0); private: void mouseMoveEvent(QMouseEvent* event) override; void wheelEvent(QWheelEvent *event) override; private: QPointF wheelRatio; };
CPP文件。本代码方法是在鼠标移动的时候保存当前鼠标位置的比例,如果鼠标不动则不改变此值。然后在按住鼠标右键同时滚动滚轮时根据前面保存的信息计算视图新的水平和垂直滚动条的值。你可能会想完全可以在wheelEvent(...)中取鼠标位置作为缩放时鼠标的当前位置,但经过测试在此取值计算误差会比较大。尚不知道为什么误差大,可能是鼠标位置是整形而缩放之后的像素位置变成浮点型取整,反复这个操作导致累计误差越来越大:
MImageView::MImageView(QWidget* parent) : QGraphicsView(parent) { setMouseTracking(true); } void MImageView::mouseMoveEvent(QMouseEvent* event) { QPointF scp = mapToScene(event->pos()); QRectF rect = sceneRect(); wheelRatio.setX((scp.x() - rect.x()) / rect.width()); wheelRatio.setY((scp.y() - rect.y()) / rect.height()); } void MImageView::wheelEvent(QWheelEvent *event) { if (!(event->buttons() & Qt::MouseButton::RightButton)) { QGraphicsView::wheelEvent(event); return; } QGraphicsScene* sc = scene(); if (event->delta() > 0) { for (auto item : sc->items()) { item->setScale(item->scale() + 0.1); } } else { for (auto item : sc->items()) { item->setScale(item->scale() - 0.1); } } setSceneRect(sc->itemsBoundingRect()); QScrollBar *hbar = horizontalScrollBar(); int hmin = hbar->minimum(); int hmax = hbar->maximum(); int hstep = hbar->pageStep(); QScrollBar *vbar = verticalScrollBar(); int vmin = vbar->minimum(); int vmax = vbar->maximum(); int vstep = vbar->pageStep(); qreal xval = hmin + (hmax - hmin + hstep) * wheelRatio.x() - event->pos().x(); qreal yval = vmin + (vmax - vmin + vstep) * wheelRatio.y() - event->pos().y(); hbar->setValue(xval); vbar->setValue(yval); }
在主窗口初始化时操作如下。下方代码中QtTest是主窗口类,ui.gvHost是MImageView控件。它的作用就是给图形视图中添加一张图片:
QtTest::QtTest(QWidget *parent) : QMainWindow(parent) { ui.setupUi(this); QGraphicsScene* scene = new QGraphicsScene(ui.gvHost); scene->addPixmap(QPixmap(u8"2.bmp")); ui.gvHost->setScene(scene); }