Qt QGraphicsView移动、缩放

原链接

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

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

一、缩放

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

void QGraphicsView::scale(qreal sx, qreal sy)

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

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撑得更大,不能自动变小。

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。

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事件再次传入视图。上面的例子也是用到了这样的方法。

  • 21
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个基于Qt的例子,展示如何实现旋转矩形后,鼠标拖动改变矩形但依旧保持不变的效果。您可以根据自己的需求进行修改: ```C++ #include <QGraphicsView> #include <QGraphicsScene> #include <QGraphicsRectItem> #include <QApplication> #include <QMouseEvent> #include <QPointF> #include <cmath> class RotatableRect : public QGraphicsRectItem { public: RotatableRect(qreal x, qreal y, qreal w, qreal h): QGraphicsRectItem(x, y, w, h) { setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemSendsGeometryChanges); setFlag(QGraphicsItem::ItemIsSelectable); angle = 0.0; center = rect().center(); } enum { Type = UserType + 1 }; int type() const override { return Type; } void mousePressEvent(QGraphicsSceneMouseEvent *event) override { if (event->button() == Qt::LeftButton) { prev_mouse_pos = event->scenePos(); prev_pos = pos(); } QGraphicsRectItem::mousePressEvent(event); } void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override { if (event->buttons() & Qt::LeftButton) { QPointF mouse_diff = event->scenePos() - prev_mouse_pos; QPointF new_pos = prev_pos + mouse_diff; // calculate new coordinates in rotated system QPointF delta = new_pos - pos(); qreal dx = delta.x(); qreal dy = delta.y(); qreal cos_a = std::cos(-angle); qreal sin_a = std::sin(-angle); qreal x = cos_a * dx - sin_a * dy; qreal y = sin_a * dx + cos_a * dy; QPointF new_pos_rotated = pos() + QPointF(x, y); // update item position setPos(new_pos_rotated - center); } QGraphicsRectItem::mouseMoveEvent(event); } void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override { QGraphicsRectItem::mouseReleaseEvent(event); } QRectF boundingRect() const override { QRectF rect = QGraphicsRectItem::boundingRect(); qreal margin = 2.0; return rect.adjusted(-margin, -margin, margin, margin); } void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override { painter->save(); painter->translate(center); painter->rotate(angle); painter->translate(-center); QGraphicsRectItem::paint(painter, option, widget); painter->restore(); } QVariant itemChange(GraphicsItemChange change, const QVariant &value) override { if (change == QGraphicsItem::ItemPositionChange) { QPointF new_pos = value.toPointF(); // calculate new coordinates in rotated system QPointF delta = new_pos - pos(); qreal dx = delta.x(); qreal dy = delta.y(); qreal cos_a = std::cos(angle); qreal sin_a = std::sin(angle); qreal x = cos_a * dx - sin_a * dy; qreal y = sin_a * dx + cos_a * dy; QPointF new_pos_rotated = pos() + QPointF(x, y); // update item position return new_pos_rotated - center; } else if (change == QGraphicsItem::ItemTransformChange) { angle = transform().rotateRadians(0.0, Qt::ZAxis); center = rect().center(); } return QGraphicsRectItem::itemChange(change, value); } private: QPointF prev_mouse_pos; QPointF prev_pos; qreal angle; QPointF center; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QGraphicsScene scene; QGraphicsView view(&scene); view.setRenderHint(QPainter::Antialiasing); view.setDragMode(QGraphicsView::RubberBandDrag); RotatableRect *rect = new RotatableRect(-50, -50, 100, 100); rect->setBrush(Qt::red); scene.addItem(rect); view.setSceneRect(-200, -200, 400, 400); view.show(); return a.exec(); } ``` 在这个例子中,我们继承了QGraphicsRectItem类,重载了鼠标事件和位置变化事件,并且在paint()函数中实现了旋转矩形的绘制。在鼠标事件中,我们使用了旋转矩形的中心点和角度,将鼠标移动的距离转化为相对于旋转后的坐标系的坐标偏移量,并根据新的坐标计算出旋转后的矩形位置和大小。 最后,我们创建了一个RotatableRect对象,并将其添加到QGraphicsScene中。通过QGraphicsView来显示场景,您可以通过鼠标进行拖动并改变矩形的大小和位置,而矩形旋转后,鼠标拖动改变矩形但依旧不变。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值