如何使QGraphicsItem不随QGraphicsView放大缩小而改变大小

一、简述

在使用QGraphicsView过程中,有时候我们需要对view进行缩放,但是对于一般正常的加入view中的item都会随着view的大小变化而变化,但是如果我们想让某些item不随view的缩放进行改变怎么办呢?

很简单,下面讲述两种方法供大家参考学习。

二、代码之路

首先讲述如何通过鼠标滚轮使view放大缩小。很简单,继承QGraphicsView类,然后重写wheelEvent()事件即可。

void MyGraphicsView::wheelEvent(QWheelEvent *event)
{
    // 当前放缩倍数;
    qreal scaleFactor = this->matrix().m11();
    int wheelDeltaValue = event->delta();
    // 向上滚动,放大;
    if (wheelDeltaValue > 0)
    {
        this->scale(1.2, 1.2);
    }
    // 向下滚动,缩小;
    else
    {
        this->scale(1.0 / 1.2, 1.0 / 1.2);
    }
}

方法一:QGraphicsItem::ItemIgnoresTransformations

    QGraphicsItem* item = new QGraphicsItem;
    item->setFlag(QGraphicsItem::ItemIgnoresTransformations);

Qt助手中setFlag()方法的介绍:

这里写图片描述

这里我们看一下需要使用到的ItemIgnoresTransformations参数的说明:

这里写图片描述

这里我们看到,如果给item设置了这个参数,那个这个item就会忽略通过继承从父类得到的transformations,这里的transformations具体包含三个部分rotation、 zoom、shear,这里将会忽略这三个参数的作用。

所以如果你只想忽略某一个,比如只忽略zoom(放大,缩小),其他的想保留,那么就要看一下方法二。


方法二:通过重写QGraphicsItem的paint()方法

其实我们需要知道,itempaint方法什么时候会调用,从哪里调用。我们可以跟踪一下源码,其实第一步是先进入到QGraphicsViewpaintEvent事件中,然后在这里创建QPainter对象,并未painter设置当前的transformations(rotation、 zoom、shear),然后将painter对象传给scene对象,通过调用scenedrawItems方法,在drawItems方法中去绘制每一个item(通过调用item的paint方法),这个过程比较复杂,具体大家可以看一下源码。

这里写图片描述

这里写图片描述

通过重写QGraphicsItempaint()方法,实现QGraphicsItem不随QGraphicsView放大缩小而改变大小。同理,这里也可以重新设置rotation或者shear

void CustomItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget /* = Q_NULLPTR */)
{
    double scaleFactor = 1.0 / painter->matrix().m11();

    // view放大或者缩小,我们在paint方法中进行反操作;
    // 比如view放大了一倍,我们就缩小一倍;view缩小一半,我们就放大一倍;
    painter->scale(scaleFactor, scaleFactor);
    __super::paint(painter, option, widget);
}

测试代码

void CustomItemTest::initView()
{
    // 自定义线为红色;
    // CustomItem中重写了paint方法,所以不会随QGraphicsView放大缩小而改变大小;
    CustomItem* myItem = new CustomItem;
    myItem->setLine(QLine(QPoint(200, 200), QPoint(300, 300)));
    QPen pen;
    pen.setColor(Qt::red);
    myItem->setPen(pen);

    // Qt LineItem 为绿色;
    QGraphicsLineItem* lineItem = new QGraphicsLineItem;
    lineItem->setLine(QLine(QPoint(200, 300), QPoint(300, 400)));
    pen.setColor(Qt::blue);
    lineItem->setPen(pen);

    // 方法一
    //  lineItem->setFlag(QGraphicsItem::ItemIgnoresTransformations);

    QGraphicsRectItem* rectItem = new QGraphicsRectItem;
    rectItem->setRect(QRect(QPoint(200, 400), QPoint(300, 500)));

    CustomView* myView = new CustomView;
    QGraphicsScene* scene = new QGraphicsScene;
    scene->addItem(myItem);
    scene->addItem(lineItem);
    scene->addItem(rectItem);
    myView->setScene(scene);

    QHBoxLayout* myLayout = new QHBoxLayout(this);
    myLayout->addWidget(myView);

}

我们看一下效果,下图中,红色线不会随着随QGraphicsView放大缩小而改变大小,而蓝色的线会随着view放大缩小而改变大小,不仅改变了线的长度还有线的宽度。

这里写图片描述

然后我们把测试代码中一行注释的代码解开注释,即我们上面讲到的方法一,看一下效果,两条线的宽度和长度都不会改变。

这里写图片描述


这里我举了线条的例子,像其他Item,比如QGraphicsRectItem、QGraphicsEllipseItem等都可以使用以上两种方法使其不随QGraphicsView放大缩小而改变大小。

但是如果像地图这种对于线条,只想保持线条的宽度不变,这个就必须使用方法二了,这里也需要做一些必要的修改了。


void CustomItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    double scaleFactor =  painter->matrix().m11();

    // QPainter::pen()方法需要注意一下
    // 如果之前没有给item设置画笔的话,返回的是一个线宽为0,颜色为黑色,线型为实现的画笔;
    // 具体看下行Qt助手中的说明;
    // Returns the item's pen, or a black solid 0-width pen if no pen has been set.

    // 一般的话如果我们想要给线宽设成某一宽度,并且线宽不随view放大缩小而改变,那就在view放大缩小时重新设置线宽;


    // 获取到当前的线宽,这里的线宽其实还是之前设置的线宽值;
    // 比如我们之前设置线宽为 2 ,这里返回的线宽还是 2 ,但是当前的缩放比例变了;
    // 其实当前的线宽就相当于 penWidth * scaleFactor;
    // 所以如果我们想要让线宽保持不变,那就需要进行转换,即 penWidth = penWidth / scaleFactor;
    QPen myPen = this->pen();
    // 重新设置画笔线宽;
    myPen.setWidthF(myPen.widthF() / scaleFactor);
    painter->setPen(myPen);
    // 这里就不能用__super::paint(painter, option, widget);方法了;
    // 因为源码中会重新调用painter->setPen(this->pen());导致线宽设置无效了;
    painter->drawLine(this->line());

    // 如果这里想调用__super::paint(painter, option, widget);
    // 那就定义一个成员变量m_penWidth来保存初次设置画笔的宽度(写一个set方法);
    // myPen.setWidthF(m_penWidth / scaleFactor);
    // this->setPen(pen);
    // __super::paint(painter, option, widget);
}

效果图:

这里写图片描述

我们看到红线的长度会随着view的缩放而变化,但是线宽却一直保持不变,可以对比下面的矩形,四条边的线宽会不断变化,而蓝色的线只设置了QGraphicsItem::ItemIgnoresTransformations,就一直保持不变。

还有一点需要注意的是,如果给画笔的线宽设置为0(见下方代码),那么在view缩放时,线宽就永远保持为1,不会变化,虽然也达到了一样的效果,但是不能设置线宽为其他值了,比如设置线宽为2、5等等。

    QPen myPen = this->pen();
    myPen.setWidth(0);
    painter->setPen(myPen);
    painter->drawLine(this->line());

代码下载

如何使QGraphicsItem不随QGraphicsView放大缩小而改变大小

(本来上传代码时填写积分为0的,现在没有了这个选项,最低一分,如果没有积分的小伙伴@我)

  • 29
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
您可以通过以下步骤实现在鼠标悬停时放大图片: 1. 为需要放大的图片添加一个QGraphicsPixmapItem对象。 2. 监听QGraphicsView的鼠标移动事件,并在事件处理函数中获取鼠标位置。 3. 在鼠标移动事件处理函数中,计算鼠标位置是否在图片所在的矩形区域内。如果在,则将图片的缩放比例增加一定值,然后重新设置QGraphicsPixmapItem的缩放比例。 4. 在鼠标离开事件处理函数中,将图片的缩放比例还原为初始值。 以下是一个示例代码: ```python from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem from PyQt5.QtGui import QPixmap from PyQt5.QtCore import Qt class MyView(QGraphicsView): def __init__(self, parent=None): super(MyView, self).__init__(parent) self.setMouseTracking(True) self.setScene(QGraphicsScene(self)) self.pixmap_item = QGraphicsPixmapItem(QPixmap("image.png")) self.scene().addItem(self.pixmap_item) self.zoom_factor = 1.0 def mouseMoveEvent(self, event): pos = self.mapToScene(event.pos()) if self.pixmap_item.boundingRect().contains(pos): self.zoom_factor += 0.1 self.pixmap_item.setTransformOriginPoint(event.pos()) self.pixmap_item.setScale(self.zoom_factor) super(MyView, self).mouseMoveEvent(event) def leaveEvent(self, event): self.zoom_factor = 1.0 self.pixmap_item.setScale(self.zoom_factor) super(MyView, self).leaveEvent(event) ``` 要实现使用鼠标滚轮在鼠标位置放大缩小,请按照以下步骤操作: 1. 监听QGraphicsView的鼠标滚轮事件,并在事件处理函数中获取鼠标位置。 2. 计算鼠标位置在场景坐标系中的位置,并以此为中心进行缩放。 3. 通过设置QGraphicsView的缩放比例实现放大缩小。 以下是一个示例代码: ```python from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene from PyQt5.QtGui import QTransform from PyQt5.QtCore import Qt class MyView(QGraphicsView): def __init__(self, parent=None): super(MyView, self).__init__(parent) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self.setMouseTracking(True) self.setScene(QGraphicsScene(self)) self.zoom_factor = 1.0 def wheelEvent(self, event): pos = self.mapToScene(event.pos()) delta = event.angleDelta().y() zoom_delta = delta / 1200.0 self.zoom_factor += zoom_delta if self.zoom_factor < 0.1: self.zoom_factor = 0.1 elif self.zoom_factor > 10.0: self.zoom_factor = 10.0 self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self.setTransformationAnchor(QGraphicsView.NoAnchor) self.setResizeAnchor(QGraphicsView.NoAnchor) self.scale(1+zoom_delta, 1+zoom_delta) self.centerOn(pos) def centerOn(self, pos): viewport_rect = self.viewport().rect() scene_rect = self.scene().sceneRect() center = QTransform().map(pos).toPoint() dx = viewport_rect.center().x() - center.x() dy = viewport_rect.center().y() - center.y() new_center = self.mapToScene(viewport_rect.center() + QPoint(dx, dy)) if scene_rect.contains(new_center): self.setSceneRect(scene_rect) super(MyView, self).centerOn(new_center) ``` 希望这可以帮助到您!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值