python QT 图片缩放,移动

article/2025/9/23 6:54:49

python QT graphicsView控件实现图片的缩放与移动

  • 1、效果图
  • 2、界面搭建
  • 3、实现方法
    • 3.1、构建处理图元的类
    • 3.1、绘制图像
    • 3.2、拖拽方法实现
    • 3.3、缩放方法实现
  • 4、调用方法

1、效果图

选择图片后可在graphicsView窗口中显示选择的图片,可以用鼠标拖拽图片。当鼠标停在图片上时滚动滑轮,以鼠标位置为中心缩放;当鼠标不在图片上时滚动滑轮,以图片自身中心进行缩放。
在这里插入图片描述

2、界面搭建

利用Qt designer 添加graphicsView控件。整个界面由两个垂直布局的groupBox组成,上面的groupBox中仅有一个graphicsView控件(即下图中红箭头所指的控件),下面的groupBox仅包含一个按钮,用以选择图片。
在这里插入图片描述

3、实现方法

3.1、构建处理图元的类

该类继承于QWidget。构造方法中除了图形界面初始化外,还将图形视图的内边距和边界去除、改变图形视图的对齐方式、设置场景大小和图形视图大小一致,同时接管图形场景的mousePressEvent 、mouseMoveEvent 、wheelEvent 方法(用来实现鼠标点击与滑轮滚动的自定义事件),具体代码如下:

class IMG_WIN(QWidget):def __init__(self,graphicsView):super().__init__()self.graphicsView=graphicsViewself.graphicsView.setStyleSheet("padding: 0px; border: 0px;")  # 内边距和边界去除self.scene = QtWidgets.QGraphicsScene(self)self.graphicsView.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)  # 改变对齐方式self.graphicsView.setSceneRect(0, 0, self.graphicsView.viewport().width(),self.graphicsView.height())  # 设置图形场景大小和图形视图大小一致self.graphicsView.setScene(self.scene)self.scene.mousePressEvent = self.scene_MousePressEvent  # 接管图形场景的鼠标点击事件# self.scene.mouseReleaseEvent = self.scene_mouseReleaseEventself.scene.mouseMoveEvent = self.scene_mouseMoveEvent	# 接管图形场景的鼠标移动事件self.scene.wheelEvent = self.scene_wheelEvent			# 接管图形场景的滑轮事件self.ratio = 1  # 缩放初始比例self.zoom_step = 0.1  # 缩放步长self.zoom_max = 2  # 缩放最大值self.zoom_min = 0.2  # 缩放最小值self.pixmapItem=None

3.1、绘制图像

    def addScenes(self,img):  # 绘制图形self.org = imgif self.pixmapItem != None:originX = self.pixmapItem.x()originY = self.pixmapItem.y()else:originX, originY = 0, 0  # 坐标基点self.scene.clear()	# 清除当前图元img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # opencv读取的bgr格式图片转换成rgb格式self.pixmap = QtGui.QPixmap(QtGui.QImage(img[:], img.shape[1], img.shape[0], img.shape[1] * 3,QtGui.QImage.Format_RGB888))  # 转化为qlbel格式self.pixmapItem = self.scene.addPixmap(self.pixmap)self.pixmapItem.setScale(self.ratio)  # 缩放self.pixmapItem.setPos(originX, originY)

在IMG_WIN类中添加addScenes方法,并添加img参数。调用该方法即可在窗口中显示图片,传入的img参数是用opencv读入的图片对象。

3.2、拖拽方法实现

    def scene_MousePressEvent(self, event):if event.button() == QtCore.Qt.LeftButton:  # 左键按下# print("鼠标左键单击")  # 响应测试语句# print(event.scenePos())self.preMousePosition = event.scenePos()  # 获取鼠标当前位置# if event.button() == QtCore.Qt.RightButton:  # 右键按下#     print("鼠标右键单击")  # 响应测试语句def scene_mouseMoveEvent(self, event):if event.buttons() == QtCore.Qt.LeftButton:# print("左键移动")  # 响应测试语句self.MouseMove = event.scenePos() - self.preMousePosition  # 鼠标当前位置-先前位置=单次偏移量self.preMousePosition = event.scenePos()  # 更新当前鼠标在窗口上的位置,下次移动用self.pixmapItem.setPos(self.pixmapItem.pos() + self.MouseMove)  # 更新图元位置

拖拽移动的关键是获取鼠标按住并拖动的位移量。scene_MousePressEvent在鼠标点击时触发,通过event.scenePos()获取点击的位置;scene_mouseMoveEvent在鼠标按下并移动过程中触发,当前位置与之前位置作差即可得到鼠标的位移量。之后通过setPos()方法更新图元的位置,就实现了拖拽移动的功能。注意scene_MousePressEvent和scene_mouseMoveEvent中判断按键状态一个是button,一个是buttons,两者是不一样的,大家可自行查阅一下。

3.3、缩放方法实现

        # 定义滚轮方法。当鼠标在图元范围之外,以图元中心为缩放原点;当鼠标在图元之中,以鼠标悬停位置为缩放中心def scene_wheelEvent(self, event):angle = event.delta() / 8  # 返回QPoint对象,为滚轮转过的数值,单位为1/8度if angle > 0:# print("滚轮上滚")self.ratio += self.zoom_step  # 缩放比例自加if self.ratio > self.zoom_max:self.ratio = self.zoom_maxelse:w = self.pixmap.size().width() * (self.ratio - self.zoom_step)h = self.pixmap.size().height() * (self.ratio - self.zoom_step)x1 = self.pixmapItem.pos().x()  # 图元左位置x2 = self.pixmapItem.pos().x() + w  # 图元右位置y1 = self.pixmapItem.pos().y()  # 图元上位置y2 = self.pixmapItem.pos().y() + h  # 图元下位置if event.scenePos().x() > x1 and event.scenePos().x() < x2 \and event.scenePos().y() > y1 and event.scenePos().y() < y2:  # 判断鼠标悬停位置是否在图元中# print('在内部')self.pixmapItem.setScale(self.ratio)  # 缩放a1 = event.scenePos() - self.pixmapItem.pos()  # 鼠标与图元左上角的差值a2=self.ratio/(self.ratio- self.zoom_step)-1    # 对应比例delta = a1 * a2self.pixmapItem.setPos(self.pixmapItem.pos() - delta)# ----------------------------分维度计算偏移量-----------------------------# delta_x = a1.x()*a2# delta_y = a1.y()*a2# self.pixmapItem.setPos(self.pixmapItem.pos().x() - delta_x,#                        self.pixmapItem.pos().y() - delta_y)  # 图元偏移# -------------------------------------------------------------------------else:# print('在外部')  # 以图元中心缩放self.pixmapItem.setScale(self.ratio)  # 缩放delta_x = (self.pixmap.size().width() * self.zoom_step) / 2  # 图元偏移量delta_y = (self.pixmap.size().height() * self.zoom_step) / 2self.pixmapItem.setPos(self.pixmapItem.pos().x() - delta_x,self.pixmapItem.pos().y() - delta_y)  # 图元偏移else:# print("滚轮下滚")self.ratio -= self.zoom_stepif self.ratio < 0.2:self.ratio = 0.2else:w = self.pixmap.size().width() * (self.ratio + self.zoom_step)h = self.pixmap.size().height() * (self.ratio + self.zoom_step)x1 = self.pixmapItem.pos().x()x2 = self.pixmapItem.pos().x() + wy1 = self.pixmapItem.pos().y()y2 = self.pixmapItem.pos().y() + h# print(x1, x2, y1, y2)if event.scenePos().x() > x1 and event.scenePos().x() < x2 \and event.scenePos().y() > y1 and event.scenePos().y() < y2:# print('在内部')self.pixmapItem.setScale(self.ratio)  # 缩放a1 = event.scenePos() - self.pixmapItem.pos()  # 鼠标与图元左上角的差值a2=self.ratio/(self.ratio+ self.zoom_step)-1    # 对应比例delta = a1 * a2self.pixmapItem.setPos(self.pixmapItem.pos() - delta)# ----------------------------分维度计算偏移量-----------------------------# delta_x = a1.x()*a2# delta_y = a1.y()*a2# self.pixmapItem.setPos(self.pixmapItem.pos().x() - delta_x,#                        self.pixmapItem.pos().y() - delta_y)  # 图元偏移# -------------------------------------------------------------------------else:# print('在外部')self.pixmapItem.setScale(self.ratio)delta_x = (self.pixmap.size().width() * self.zoom_step) / 2delta_y = (self.pixmap.size().height() * self.zoom_step) / 2self.pixmapItem.setPos(self.pixmapItem.pos().x() + delta_x, self.pixmapItem.pos().y() + delta_y)

看似很长,其实很多都是类似的,不要慌。
首先说明一下图元是通过setScale()方法实现缩放的,但默认是以左上角为原点进行缩放,我没有找到可以调整缩放中心的方法。所以要实现以鼠标为中心缩放,是先缩放再平移。若有更简便的方法,还请大佬不吝赐教。
首先通过event.delta()获取滚轮的拨动方向,这个值的大小不必在意,主要是正负号判断向上还是向下滚动。我们以大于0(放大)为例说明一下实现的步骤。首先是限幅判断,如果放大比例超过上限则不作处理。(x1,y1)为缩放前图元左上角坐标,(x2,y2)为缩放前图元右下角坐标。之后用event.scenePos()获取鼠标当前的位置,与(x1,y1)和(x2,y2)比较就能确定鼠标是不是在图元当中,若是则以鼠标为中心缩放,若鼠标在图元之外则以图元中心进行缩放。

第一种情况是以鼠标位置为中心缩放
在这里插入图片描述
假设现在由红框放大至蓝框,放大比例为k。鼠标放置在红点位置,那么放大过后红点对应到蓝点,若想鼠标停放的位置为蓝点,则需要将蓝框位移(delta_x,delta_y),这样就达到了以鼠标为中心缩放的效果。

在这里插入图片描述
我们以x轴偏移量计算为例。上图中有红线放大到蓝线,比例依然为k。x0,x1为线段端点。计算出任意点x对应的点x2,x2-x即为x轴的偏移量delta_x。
x − x 0 x 2 − x 0 = x 1 − x 0 k ( x 1 − x 0 ) \frac {x-x_0} {x_2-x_0}=\frac {x_1-x_0} {k(x_1-x_0)} x2x0xx0=k(x1x0)x1x0
x 2 = k ( x − x 0 ) + x 0 x_2=k(x-x_0)+x_0 x2=k(xx0)+x0
d e l t a x = x 2 − x = ( k − 1 ) ( x − x 0 ) deltax=x_2-x=(k-1)(x-x_0) deltax=x2x=(k1)(xx0)
同理可以算出 d e l t a y = ( k − 1 ) ( y − y 0 ) deltay=(k-1)(y-y_0) deltay=(k1)(yy0)

a1 = event.scenePos() - self.pixmapItem.pos()  # 鼠标与图元左上角的差值
a2=self.ratio/(self.ratio- self.zoom_step)-1    # 对应比例
delta = a1 * a2
self.pixmapItem.setPos(self.pixmapItem.pos() - delta)

即代码中 a 1 = x − x 0 , a 2 = k − 1 a1=x-x_0,a2=k-1 a1=xx0,a2=k1。最后通过setPos()方法将偏移量加上去就OK了。

第二种情况是以图片自身中心缩放
当鼠标不在图元上时以鼠标为中心缩放就没有意义了,这时切换到以图元自身中心缩放。这时套用上面的公式也是可以的,令 x = x 0 + x 1 2 x=\frac{x_0+x_1}{2} x=2x0+x1即可。但其实如果按图元中心缩放,每次的缩放比例又一致的话,那么每次的位移量其实都是放大差值的一半,就不用上面麻烦的算了。

delta_x = (self.pixmap.size().width() * self.zoom_step) / 2  # 图元偏移量
delta_y = (self.pixmap.size().height() * self.zoom_step) / 2
self.pixmapItem.setPos(self.pixmapItem.pos().x() - delta_x,self.pixmapItem.pos().y() - delta_y)  # 图元偏移

4、调用方法

class GUI(QWidget):def __init__(self):super().__init__()self.ui = QUiLoader().load('img_view.ui')self.graphic=IMG_WIN(self.ui.graphicsView)	# 实例化IMG_WIN类self.ui.pushButton.clicked.connect(self.select_img)def select_img(self):filePath, _ = QFileDialog.getOpenFileName(self.ui,  # 父窗口对象"选择你要上传的图片",  # 标题r"E:\picture\test",  # 起始目录"图片类型 (*.png *.jpg *.bmp)"  # 选择类型过滤项,过滤内容在括号中)if filePath == '':returnelse:img = cv2.imread(filePath)self.graphic.addScenes(img)

只需在主界面中将IMG_WIN类实例化,并传入预先定义好的graphicsView对象即可。需要显示图片则调用IMG_WIN类中的addScenes方法,传入的为opencv读取的图片,其他格式则需要另外的修改才能正常显示。

本人也是初学,若有不对的地方欢迎大佬指正!,完整代码已上传至GitHub,https://github.com/risemeup/pyside2View。如果能帮到素未谋面的你,点个星星,交个朋友。


http://chatgpt.dhexx.cn/article/F534ksXh.shtml

相关文章

Unity UI修改Image中的图片资源

Unity UI修改Image中的图片资源 一、取资源文件 把资质文件放到Assets文件夹下Resources文件中。 二、在属性面板下修改图片类型 三、在脚本中修改需要修改的资源 Sprite sprite Resources.Load(“Images/2”); //Images文件夹下名为2的图片 Image2.sprite sprite; //修改…

Linux命令scp用法

本文主要讲的是scp用法如果哪里不对欢迎指出&#xff0c;主页https://blog.csdn.net/qq_57785602?typeblog scp 可以在win系统使用&#xff0c;本文百分之八十写的是win系统怎么使用&#xff0c;在本地上到服务器文件,从服务器下载文件到本地 用工具连接到公司服务器时&#x…

linux远程cp命令,Linux cp scp命令使用

cp 拷贝命令&#xff0c;用来对文件或者子目录进行拷贝操作的。 scp 是secure copy的简写&#xff0c;用于在Linux下进行远程拷贝文件的命令&#xff0c;和它类似的命令有cp&#xff0c;不过cp只是在本机进行拷贝不能跨服务器&#xff0c;而且scp传输是加密的。可能会稍微影…

linux 使用scp命令,Linux scp命令使用实例汇总

Linux下要实现两台服务器之间的文件传送&#xff0c;使用scp命令就可以了&#xff0c;在Linux系统中&#xff0c;scp命令的用法简单而又实用&#xff0c;系统之家就给大家介绍下如何使用scp命令进行Linux服务器之间的文件传送。 scp是secure copy的简写&#xff0c;用于在Linux…

linux中scp命令用法

问题&#xff1a;经常用到文件分发到另一个机器&#xff0c;怎么做&#xff1f; 每次连接一个机器&#xff0c;把压缩包上传上去&#xff0c;然后解压修改&#xff0c;这样在太麻烦。linux的cp命令可以复制文件&#xff0c;能不能吧修改好的文件复制过去呢&#xff1f; 一、cp…

scp命令的使用

Linux scp 命令用于 Linux 之间复制文件和目录&#xff1b;scp 是 secure copy 的缩写, scp 是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令&#xff1b;scp 是加密的&#xff0c;rcp 是不加密的&#xff0c;scp 是 rcp 的加强版。 用法&#xff1a; scp [可选参数]…

scp命令使用方法

scp 命令是用于通过 SSH 协议安全地将文件复制到远程系统和从远程系统复制文件到本地的命令。使用 SSH 意味着它享有与 SSH 相同级别的数据加密&#xff0c;因此被认为是跨两个远程主机传输文件的安全方式。 基本语法 下面提供了 SCP 命令的基本语法&#xff1a; $ scp [opt…

unity仓库管理简易模型(一)

1. 环境 unity2018 2. 运行截图 3. 功能 1. 场景漫游 提供第一视角模式和自由模式。第一视角使用unity自带的角色控制器包实现角色移动视角移动&#xff0c;自由模式实现鼠标拖拽&#xff0c;场景缩放。 2. 商品定位 通过输入商品信息&#xff0c;在三维场景中定位到商品…

计算机网络基础(类别 | 性能指标 | OSI模型初识)

目录 计算机网络类别 根据作用的范围分类 局域网&#xff08;LAN&#xff09; 广域网&#xff08;WAN&#xff09; 根据使用者分类 按照拓扑结构来分类 按照交换方式分类 按照工作方式分类 性能指标 什么是带宽 速率 吞吐量 时延 传播时延和带宽的乘积 往返时间…

(附源码)springboot学生宿舍管理系统 毕业设计 211955

摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作…

数据蛙9套SQL面试题笔记

数据分析SQL面试题目9套汇总 题目来源&#xff1a; https://www.jianshu.com/p/0f165dcf9525 关于这套题的笔记 一、 解题思路&#xff1a; 1、用concat实现连接 2、还需要按照“用户号”分组&#xff0c;将每组中的前两个“场景”号间接&#xff0c;这时需要用到 GROUP_C…

tp6中无限极分类里面的获取多级分类数据

作者&#xff1a;陈业贵 华为云享专家 51cto(专家博主 明日之星 TOP红人) 文章目录 前言一、什么是多级分类数据&#xff1f;二、使用步骤sql代码2.效果图 总结 前言 和大家共同完成获取多级分类数据 一、什么是多级分类数据&#xff1f; 就是很多很多的数据&#xff0c;按照…

基于Gazebo的无人机管道检测

管道检测正式版本 1.需求分析 面对管道沿线地势起势大、道路崎岖难走&#xff0c;沿途穿越河流、沟谷、沼泽地纵多&#xff0c; 杂草植被茂密&#xff0c;无巡检通道等现状&#xff0c;人工巡检暴露出明显缺陷&#xff0c;车辆无法到达,需要跋山涉水徒步进行&#xff0c;巡护时…

【期末复习】第二章 关系数据库

博主介绍&#xff1a; – 我是了 凡 微信公众号【了凡银河系】期待你的关注。未来大家一起加油啊~ 文章目录 2.1 关系数据结构及形式化定义2.1.1 关系2.1.2 关系模式2.1.3 关系数据库2.1.4 关系模型的存储结构 2.2 关系操作(了解关系操作具体怎么做)2.2.1 基本的关系操作2.2.2…

SQL数据库的整体结构、索引、MVCC、锁、日志、查询优化,三大范式等

关系型数据库和非关系型数据库 SQL:关系型数据库指的是使用关系模型&#xff08;二维表格模型&#xff09;来组织数据的数据库。(mysql,sqlserver,sqllite,oracle) 关系数据库的优点&#xff1a; 容易理解&#xff0c;符合正常思维方式&#xff1b;都是用表格形式&#xff0c;格…

ubuntu mysql执行sql文件

1、altt打开终端&#xff0c;输入mysql -u root -p回车&#xff0c;输入密码再次回车&#xff0c;进入mysql。 2、如果sql文件中包含建库和建表语句&#xff0c;直接输入source 路径/xxx.sql&#xff0c;比如我的是source /home/dzh/Software/DataBase_Creater.sql&#xff1b;…

Mac OS快速查看当前连接IP等信息

常见方式是查看IP等信息可以打开系<统偏好设置> 内的 <网络> 但是可以使用快捷方式直接点击屏幕上方的WiFi标志 按住Option键 点击此标识图标 (屏幕右上侧)

mac本在终端查看本地ip

在终端输入ifconfig即可查看本机地址

[Mac OS X] 如何在终端查看 Mac OS 版本信息

本文转载至&#xff1a;https://www.cyberciti.biz/faq/mac-osx-find-tell-operating-system-version-from-bash-prompt/ use ssh client to login into my Mac Min server without GUI. How can I tell what version of Mac OS X operating system am I using command promp…