Qt QGraphicsView移动、缩放

原链接

首先需要明白,view在整个视图框架中的角色是用于显示scene的,所以决定了如何展示scene,包括scale()函数,用于放大缩小所展示的scene;centerOn()函数,决定scene的中心在何方。所有的操作,都不会直接改变scene,改变的只是如何展现scene。

view就像窗户,我们可以透过窗户看到窗外的景色。scene就像窗外的景色,需要窗户作为媒介才能够让我们看到,只不过这里的景色(scene)不一定会比窗户(view)大。并且,在窗户如何显示窗外的景色,也可以通过函数设置,比如我家窗户是个哈哈镜,那就可以放大缩小甚至扭曲景色。item就像是景色当中的花鸟鱼虫。如果懂PhotoShop的话,view就像是图层上的蒙版,scene就像是蒙版下的图像。

一、缩放

主要使用到scale()函数。该函数用于缩放场景,但是并不是改变了scene的大小尺寸,仅仅是改变了显示比例。

cpp 复制代码
void QGraphicsView::scale(qreal sx, qreal sy)

我们需要两个参数,sx,sy,指的是沿x,y缩放的比例,而不是大小。举个栗子:

cpp 复制代码
void MyView::wheelEvent(QWheelEvent *event)
{
    int wheelValue = event->angleDelta().y();
    double ratio = (double)wheelValue / (double)1200 + 1;
    scale(ratio, ratio);
}

MyView继承了QGraphicsView,并重写wheelEvent。每次滚动滚轮,都将放大原来的1.1倍,或是缩小原来的0.9倍。

二、移动

主要使用到centerOn()函数,用于将对应坐标的scene显示到view的中心位置。

坐标:

view和scene的原点都位于左上角。

特点:

在Qt的视图框架中,如果在scene中直接添加一个item,scene的大小就是这个item的boundingRect尺寸,也就是item的最大边界矩形。如果这个item是小于view的,那么这个item会显示在view的中心,就像这样。如果scene的尺寸是小于view的,也就是说view可以完全包含scene,那么是不能够通过centerOn来移动scene。只有当scene大于view,以至于view不能完全展示scene时,才可以通过centerOn移动。

如果事件传播正常,且item设置了QGraphicsItem::ItemIsMovable(可以在item的setFlag函数中设置),这个item可以直接移动,scene会自动变化大小以适应item。如果将item向scene的左上角移动,scene的原点依旧还是左上角,以view的视角来看,是scene右下角不变,向左上角延伸。如果将已经移动的item再移动回来,scene的大小也不会变化,换言之,scene只可能被item撑得更大,不能自动变小。

cpp 复制代码
void QGraphicsView::centerOn(const QPointF &pos)

cneterOn函数接受一个point参数,用于定义view的中心位置,应该展示scene坐标的什么位置。

注意,这个移动是有极限的,如果scene尺寸本身就大于view,那么不会将scene移出view的范围,换言之,如论如何移动,scene一定会完全包围view。

那么我们思路就十分清晰了。如果我们想移动一个距离,假设移动的距离为offsetPoint,获取一个当前view视图中中心点在scene的坐标,currentScenePoint = mapToScene(MyView.width() / 2, MyView.height() / 2),那么计算出新的点并传入centerOn函数,centerOn( - (currentScenePoint - offsetPoint));

下面是示例,这个示例中展示了当鼠标点击空白区域时,通过拖拽移动scene。

cpp 复制代码
void FunctionView::mousePressEvent(QMouseEvent *event)
{
    QGraphicsView::mousePressEvent(event);
    if(this->scene() == nullptr)
    {
        qDebug() << "The scene is null";
        return;
    }
    // 记录鼠标按下时的中心点坐标
    centerAnchor = mapToScene(event->pos()) - event->pos() + QPointF(width() / 2, height() / 2);
    // 记录当前鼠标在view中的位置,用来在mouseMove事件中计算偏移
    // 此处不将view坐标转换成scene坐标的原因是优化性能,在move的过程中会产生抖动
    posAnchor = event->pos();
    isMousePressed = true;
}
void FunctionView::mouseMoveEvent(QMouseEvent *event)
{
    QGraphicsView::mouseMoveEvent(event);
    QPointF offsetPos = event->pos() - posAnchor;
    if(isMousePressed){
        setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        centerOn(centerAnchor - offsetPos);
    }
}
void FunctionView::mouseReleaseEvent(QMouseEvent *event)
{
    QGraphicsView::mouseReleaseEvent(event);
    isMousePressed = false;
}

Tips:视图框架事件的传递顺序是view->scene->item,如果需要将事件继续向后传递,使用event->ignore()是没用的,猜测因为view看做是一个控件,scene和item都是控件内的组件,ignore只能处理控件到控件的事件,但是控件内的事件无能为力。这里可以使用QGraphicsView::mousexxxEvent(event)这样的函数,将event事件再次传入视图。上面的例子也是用到了这样的方法。

相关推荐
用户805533698039 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner10 小时前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz5 天前
QML Hello World 入门示例
qt
xcyxiner8 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner9 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner9 天前
DicomViewer (添加模型类)3
qt
xcyxiner10 天前
DicomViewer (目录调整) 2
qt
xcyxiner10 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00612 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术12 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript