QGraphicsView如何使图片以鼠标为中心进行放缩

QGraphicsView如何使图片以鼠标为中心进行放缩

顺带说一句,下面的代码放在了一个继承了QMainWindow的自定义class里面,这个类包含着一个QGraphicsView部件,所有以下的代码都会以为处在类中而出现对应的首行缩进。

QGraphicsView的自带语句实现

因为QGraphicsView是从Qt整个改过来的,自然包含了所有的部件,其中就有所用到的三句话:
self.ui.graphicsView.setDragMode(QGraphicsView.ScrollHandDrag)
self.ui.graphicsView.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.ui.graphicsView.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
可以很清楚地发现,第一句是自带的拖动效果的实现,第二句是说将变换的锚点设置到鼠标的下面,第三句话说的是将图元重设大小时的锚点设置到鼠标下面,这里就将放大缩小的点设置到了鼠标的下面。这样一来就可以通过指定点进行放大缩小。

QGraphicsView的事件重写实现

那么问题来了,我如果有一个自定义的函数需要写的话,怎么跟这个已有的方法结合起来呢?类的继承是可以,但是这家伙看着也不像一个类啊,倒像是一个方法,那我们除了将_文档_找出来慢慢研读,就是自己实现所有的操作了,文档可以去官方查找,里面用的是当前鼠标坐标与整个视图坐标的比值,再通过比值确定滚动条的位置进行操作,下方的滚动条设置偏左,右边的滚动条设置偏上 ,就能使得图片进行左上为中心的变换,至于具体位置根据图元与视图的比值进行计算就可以。
但是我有一个__更简单的逻辑__,就是将图片的重绘点设置在一个变化后的点,这个点也是通过计算算出来的,但是不同的是,这个点的坐标比较容易计算,并且没有涉及到任何其他部件,只是单纯更新一个点然后进行重绘。这个点的计算方法我会详细介绍给大家。

重写过程中涉及到的事件

既然自己重写了,那么鼠标的所有事件都要自己来一遍
之前讲过图片的放大缩小和移动,但是那个放缩是改变了滑动条,不能指哪变哪,所以这次的鼠标滑动事件和鼠标移动事件都要稍微改动一下。

自定义滚轮事件的实现逻辑

第一步:提取图片的左上和右下两个点来确定计算所需要的参数:图元第一次绘制的setPos()图片转来的图片的尺寸;第二步:获取鼠标当前坐标值以进行计算;第三步:计算出差值偏移量delta;第四步:进行图元的重绘

对于滚轮事件计算的说明

滚轮事件的第三步有一个计算出delta差值,这里截出来代码给大家看看:


# -*- coding: utf-8 -*-
"""
@author:xiaoyangchicao2020
@time:2022-99-99
"""
self.item.setScale(self.ratio)
a1=self.posss - self.item.pos()
a2=self.ratio/(self.ratio-self.step)-1
self.delta = a1 * a2
self.posnow=self.item.pos()-self.delta
self.item.setPos(self.posnow)

self.ratio是一个int,说的是图片初始的大小比例,一般设为1。self.posss就是鼠标现在相对于view的坐标了,就是==mapToscne(event.pos())==这个方法返回的值,是鼠标当前坐标,是一个QPoinF,然后用这个坐标减去图元一开始所在的坐标,也就是setPos()的那个坐标点。self.step就是图片缩放的步长,太大了图片突变,我设置的是0.3,按照这个数学运算最终会得出一个点,这个点就是需要变换的坐标差值,用图元之前的坐标减去差值,就是图元现在应该重新设置的地方,就是那个self.posnow图片在这个点重绘之后就能得到以鼠标为中心的放大缩小的效果。
完整代码如下):


# -*- coding: utf-8 -*-
"""
@author:xiaoyangchicao2020
@time:2022-99-99
"""
	self.ratio = 1#缩放系数
	self.step = 0.3 #缩放步长
	self.posnow = QPointF(0, 0)  # 图片绘制位置
    def wheel_Event_tinghua(self,event):
        angle = event.angleDelta() / 8
        angley = angle.y()
        self.posss=self.ui.graphicsView.mapToScene(event.pos())
        if angley > 0:
            self.ratio+=self.step
            w = math.ceil(self.pix.width()*(self.ratio-self.step))
            h = math.ceil(self.pix.height()*(self.ratio-self.step))
            x1=self.item.pos().x()
            x2=self.item.pos().x()+w
            y1=self.item.pos().y()
            y2=self.item.pos().y()+h
            if self.posss.x() > x1 and self.posss.x() < x2 * self.ratio \
                and self.posss.y() > y1 and self.posss.y() < y2 * self.ratio:
                self.inimg = True
                self.item.setScale(self.ratio)
                value1=self.posss - self.item.pos()
                value2=self.ratio/(self.ratio-self.step)-1
                self.delta = value1 * value2
                self.posnow=self.item.pos()-self.delta
                self.item.setPos(self.posnow)
            else:
                pass
        elif angley < 0:
            self.ratio -= self.step
            w =  math.ceil(self.pix.width() * (self.ratio + self.step))
            h =  math.ceil(self.pix.height() * (self.ratio + self.step))
            x1 = self.item.pos().x()
            x2 = self.item.pos().x() + w
            y1 = self.item.pos().y()
            y2 = self.item.pos().y() + h
            if self.posss.x() > x1 and self.posss.x() < x2 * self.ratio \
                and self.posss.y() > y1 and self.posss.y()  < y2 * self.ratio:
                self.inimg = True
                self.item.setScale(self.ratio)
                value1=self.posss - self.item.pos()
                value2=self.ratio/(self.ratio+self.step)-1
                self.delta = value1 * value2
                self.posnow = self.item.pos()-self.delta
                self.item.setPos(self.posnow)
            else:
                pass

看起来很长其实只是因为判断语句和前期获取计算需要的数据进行的工作比较多,其实逻辑还是很清晰的。

好的,这是一个比较复杂的问题,需要结合PyQt5中的GraphicsView、QPixmap、QGraphicsRectItem等类来实现。下面是一个简单的实现示例代码,供参考: ```python import sys from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5.QtCore import * class ImageView(QGraphicsView): def __init__(self, parent=None): super(ImageView, self).__init__(parent) self.image = QPixmap("test.jpg") # 加载图片 self.scene = QGraphicsScene(self) self.setScene(self.scene) # 显示图片 self.pixmap_item = QGraphicsPixmapItem(self.image) self.scene.addItem(self.pixmap_item) # 设置图片缩放 self.setDragMode(QGraphicsView.ScrollHandDrag) self.setRenderHint(QPainter.Antialiasing) self.setRenderHint(QPainter.SmoothPixmapTransform) self.setRenderHint(QPainter.HighQualityAntialiasing) self.setInteractive(True) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) # 鼠标框选矩形 self.rect_item = QGraphicsRectItem() self.rect_item.setPen(QPen(Qt.red)) self.rect_item.setBrush(QBrush(Qt.red, Qt.Dense4Pattern)) self.rect_item.setZValue(1) self.scene.addItem(self.rect_item) # 信号槽连接 self.selectionChanged.connect(self.on_selection_changed) self.rect_item.mousePressEvent = self.on_rect_mouse_press self.rect_item.mouseMoveEvent = self.on_rect_mouse_move self.rect_item.mouseReleaseEvent = self.on_rect_mouse_release def on_selection_changed(self): # 获取当前选中的区域 items = self.scene.selectedItems() if len(items) == 1 and isinstance(items[0], QGraphicsRectItem): self.rect_item.setRect(items[0].rect()) else: self.rect_item.setRect(QRectF()) def on_rect_mouse_press(self, event): # 记录鼠标按下时的位置 self.last_pos = self.mapToScene(event.pos()) def on_rect_mouse_move(self, event): # 计算矩形大小 pos = self.mapToScene(event.pos()) rect = QRectF(self.last_pos, pos).normalized() self.rect_item.setRect(rect) def on_rect_mouse_release(self, event): # 计算矩形在图片上的坐标 rect = self.rect_item.rect() scale = self.transform().m11() rect = QRectF(rect.left() / scale, rect.top() / scale, rect.width() / scale, rect.height() / scale) rect = rect.intersected(QRectF(QPointF(0, 0), self.image.size())) print(rect) if __name__ == '__main__': app = QApplication(sys.argv) view = ImageView() view.show() sys.exit(app.exec_()) ``` 这段代码实现了以下功能: 1. 加载图片并显示在GraphicsView中; 2. 支持鼠标拖动来放缩图片; 3. 支持鼠标框选红色矩形,并且可以修改矩形的尺寸; 4. 计算矩形在图片上的坐标,并输出到控制台。 需要注意的是,代码中矩形的坐标是相对于图片的,如果需要得到绝对坐标,还需要考虑图片在GraphicsView中的位置和缩放比例。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

♡小羊不吃草

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

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

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

打赏作者

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

抵扣说明:

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

余额充值