QGraphicsView的缩放和拖动,以及缩放围绕鼠标所在点进行缩放

本文详细介绍了如何在Qt中实现视图与场景的交互,包括拖动和缩放功能,使用户能够自由探索无限大的场景。通过动态调整sceneRect和处理鼠标事件,实现了平滑的拖动体验和以鼠标为中心的缩放效果。

因为要实现一个功能是,拖动view所在的窗口,可以显示scene中所有的信息,在网上找了很多资料,都是在缩放后通过scrollView进行拖动的,但是这个拖动是有问题的,只是拖动对应的scrollView中的数据,这不是我要进行拖动可以看到出来viewport中的数据,还包括很多无法显示的scene的其他部分

代码如下:

.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QWheelEvent>
#include <QMouseEvent>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

    void wheelEvent(QWheelEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseDoubleClickEvent(QMouseEvent *event);

private:
    QGraphicsScene *scene;
    QGraphicsView *view;

    QWidget* sceneWidget;

    QGraphicsPixmapItem* m_bkPixmapItem;

    QPointF m_lastPointF;
    qreal m_scale = 1;
};

#endif // MAINWINDOW_H

.cpp

#include "mainwindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(1200,800);
    sceneWidget = new QWidget(this);
    sceneWidget->setStyleSheet("border:none; background:blue;");

    sceneWidget->setFixedSize(800,600);
    view = new QGraphicsView(sceneWidget);
    view->setStyleSheet("border:none; background:lightgray;");
    view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    scene = new QGraphicsScene(sceneWidget);
//    scene->setSceneRect(0,0,800,600);
    view->setScene(scene);
//    view->setSceneRect(0,0,800,600);

    m_bkPixmapItem = new QGraphicsPixmapItem();
    m_bkPixmapItem->setPixmap(QPixmap(":/3.bmp").scaled(1280,960,Qt::KeepAspectRatioByExpanding));
    scene->addItem(m_bkPixmapItem);

    view->resize(800,600);
    view->scene()->setSceneRect(0,0,800,600);
    this->setCentralWidget(sceneWidget);

}

MainWindow::~MainWindow()
{

}

void MainWindow::wheelEvent(QWheelEvent *event)
{
    view->scale(1/m_scale, 1/m_scale);
    if(event->delta() > 0){
        if(m_scale < 10){
            m_scale += 0.2;
        }else{
            m_scale = 10;
        }
    }else{
        if(m_scale > 0.2){
            m_scale -= 0.2;
        }else{
            m_scale = 0.2;
        }
    }
    view->scale(m_scale, m_scale);
    qDebug()<<m_scale;
}

void MainWindow::mousePressEvent(QMouseEvent *event)
{
    m_lastPointF = event->pos();
}

void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    QPointF disPointF = event->pos() - m_lastPointF;
    m_lastPointF = event->pos();
    view->scene()->setSceneRect(view->scene()->sceneRect().x()+disPointF.x(),view->scene()->sceneRect().y()+disPointF.y(),
                                view->scene()->sceneRect().width(),view->scene()->sceneRect().height());
    view->scene()->update();
}

void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
{

    QGraphicsPixmapItem *m_bkPixmapItem1 = new QGraphicsPixmapItem();
    m_bkPixmapItem1->setPixmap(QPixmap(":/3.bmp").scaled(1280,960,Qt::KeepAspectRatioByExpanding));
    m_bkPixmapItem1->setPos(2000,0);
    scene->addItem(m_bkPixmapItem1);
}

关键是在于,注释的两行(scene->setSceneRect(0,0,800,600); view->setSceneRect(0,0,800,600);)、添加的 (view->scene()->setSceneRect(0,0,800,600);)以及mouseMoveEvent()函数中的拖动的实现。
其实发现,整个拖动都是在对view的scene进行操作的,每次调用scene的setSceneRect()动态的设置场景的范围,就是类似在一直在调整scene通过view显示的部分,达到通过拖动事件,动态的显示整个scene的部分(scene是无限大)

程序运行示意图:

在这里插入图片描述

补充:关于缩放的特殊要求,缩放以鼠标所在点进行缩放,即放大缩小的时候,鼠标的点的位置是不变的
修改wheelEvent()函数:(所记录的缩放是脱离了view的scroll,所以网上的实现相同功能是利用了scroll来处理的,但是原理是一致的)在整个缩放的过程中,view的坐标是不变的,所以我们可以采用鼠标所在位置的前后scene坐标的值的差值来计算需要偏移的数量。代码如下:

    // 获取当前的鼠标所在的view坐标;
    QPoint prev_viewPos = event->pos();
    // 获取当前鼠标相对于scene的位置;
     QPointF prev_scenePos = this->mapToScene(prev_viewPos);
     //....计算缩放比例
    scale(scaleTemp, scaleTemp);  //缩放
    this->scene()->setSceneRect(this->mapToScene(this->rect()).boundingRect());  //调整scene,使得scene和view一直,主要是为了排除掉scroll

    //获取缩放后的scene坐标
    QPointF scenePos = this->mapToScene(prev_viewPos);
    //获取缩放前后的坐标差值,即为需要进行move的位移
    QPointF disPointF = scenePos - prev_scenePos;
//    qDebug()<<prev_scenePos<<" ::: "<<scenePos<<disPointF;
    qDebug()<<this->scene()->sceneRect();
    //调整位置
    this->scene()->setSceneRect(this->scene()->sceneRect().x()-disPointF.x(),this->scene()->sceneRect().y()-disPointF.y(),
                                this->scene()->sceneRect().width(),this->scene()->sceneRect().height());
//    emit signal_wheel(m_scale);
    this->scene()->update();

备注:
//QWheelEvent是不存在delta()函数的。QGraphicsSceneWheelEvent的才是delta()函数;
QWheelEvent中的是angleDelta()函数。两个函数是一致的

### 同步缩放拖动的实现 在 Qt 中实现两个 `QGraphicsView` 的同步缩放拖动,可以通过以下方式完成:利用 `QGraphicsView` 提供的视图变换机制(如 `scale()` 滚动条控制),结合信号与槽机制同步两个视图的状态。 #### 同步缩放 为了实现两个视图的同步缩放,可以在其中一个视图的滚轮事件中触发缩放操作,并将相同的缩放变换应用到另一个视图上。缩放操作应以鼠标位置为锚,以确保缩放过程中内容的视觉一致性。具体做法是设置视图的变换锚缩放为 `QGraphicsView::AnchorUnderMouse`,并在滚轮事件中调用 `scale()` 方法[^2]。 示例代码如下: ```cpp void MyGraphicsView::wheelEvent(QWheelEvent *event) { if (event->modifiers() & Qt::ControlModifier) { // 以鼠标位置缩放中心 setTransformationAnchor(QGraphicsView::AnchorUnderMouse); setResizeAnchor(QGraphicsView::AnchorUnderMouse); double scaleFactor = 1.1; if (event->angleDelta().y() > 0) { scale(scaleFactor, scaleFactor); } else { scale(1.0 / scaleFactor, 1.0 / scaleFactor); } // 同步另一个视图的缩放 if (otherView) { otherView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); otherView->setResizeAnchor(QGraphicsView::AnchorUnderMouse); otherView->setTransform(transform()); } } else { QGraphicsView::wheelEvent(event); } } ``` #### 同步滚动(拖动拖动同步可以通过监听视图的滚动条变化,并将这些变化同步到另一个视图的滚动条上。具体做法是连接两个视图的水平垂直滚动条的 `valueChanged` 信号到另一个视图对应滚动条的 `setValue` 槽函数[^1]。 ```cpp connect(view1->horizontalScrollBar(), &QScrollBar::valueChanged, view2->horizontalScrollBar(), &QScrollBar::setValue); connect(view1->verticalScrollBar(), &QScrollBar::valueChanged, view2->verticalScrollBar(), &QScrollBar::setValue); connect(view2->horizontalScrollBar(), &QScrollBar::valueChanged, view1->horizontalScrollBar(), &QScrollBar::setValue); connect(view2->verticalScrollBar(), &QScrollBar::valueChanged, view1->verticalScrollBar(), &QScrollBar::setValue); ``` #### 同步变换矩阵 为了确保两个视图在缩放拖动时始终保持一致的显示状态,可以使用 `setTransform()` 方法同步整个变换矩阵。这样可以避免因多次调用 `scale()` 或 `translate()` 导于变换状态不一致的问题[^4]。 ```cpp view2->setTransform(view1->transform()); ``` #### 注意事项 - 两个视图应共享同一个 `QGraphicsScene`,以确保它们显示的内容一致。 - 在同步缩放时,需要注意视图缩放的极限范围,避免过度缩放导致内容不可见或显示异常。 - 为了避免信号循环连接导致的无限循环问题,可以在同步操作时添加判断逻辑,例如只在特定条件下更新滚动条或变换矩阵。 --- ### 示例代码 以下是一个完整的示例类,展示了如何实现两个 `QGraphicsView` 的同步缩放拖动: ```cpp class SyncGraphicsView : public QWidget { Q_OBJECT public: explicit SyncGraphicsView(QWidget *parent = nullptr) : QWidget(parent) { // 创建共享的场景 QGraphicsScene *scene = new QGraphicsScene(this); scene->addRect(QRectF(0, 0, 400, 400), QPen(Qt::black), QBrush(Qt::white)); // 创建两个视图 view1 = new MyGraphicsView(this); view2 = new MyGraphicsView(this); view1->setOtherView(view2); view2->setOtherView(view1); // 设置布局 QHBoxLayout *layout = new QHBoxLayout(this); layout->addWidget(view1); layout->addWidget(view2); // 同步滚动条 connectScrollBars(); } private: void connectScrollBars() { connect(view1->horizontalScrollBar(), &QScrollBar::valueChanged, view2->horizontalScrollBar(), &QScrollBar::setValue); connect(view1->verticalScrollBar(), &QScrollBar::valueChanged, view2->verticalScrollBar(), &QScrollBar::setValue); connect(view2->horizontalScrollBar(), &QScrollBar::valueChanged, view1->horizontalScrollBar(), &QScrollBar::setValue); connect(view2->verticalScrollBar(), &QScrollBar::valueChanged, view1->verticalScrollBar(), &QScrollBar::setValue); } MyGraphicsView *view1; MyGraphicsView *view2; }; class MyGraphicsView : public QGraphicsView { Q_OBJECT public: explicit MyGraphicsView(QWidget *parent = nullptr) : QGraphicsView(parent), otherView(nullptr) { setTransformationAnchor(QGraphicsView::AnchorUnderMouse); setResizeAnchor(QGraphicsView::AnchorUnderMouse); } void setOtherView(MyGraphicsView *view) { otherView = view; } protected: void wheelEvent(QWheelEvent *event) override { if (event->modifiers() & Qt::ControlModifier) { setTransformationAnchor(QGraphicsView::AnchorUnderMouse); setResizeAnchor(QGraphicsView::AnchorUnderMouse); double scaleFactor = 1.1; if (event->angleDelta().y() > 0) { scale(scaleFactor, scaleFactor); } else { scale(1.0 / scaleFactor, 1.0 / scaleFactor); } if (otherView) { otherView->setTransform(transform()); } } else { QGraphicsView::wheelEvent(event); } } private: MyGraphicsView *otherView; }; ``` 通过上述实现,两个 `QGraphicsView` 可以实现同步缩放拖动缩放操作以鼠标位置为锚,滚动条变化同步更新,从而确保两个视图始终保持一致的显示状态。 ---
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

庐州李大爷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值