pyqt中以鼠标所在位置为锚点缩放图片

在编写涉及到图片缩放的pyqt程序时,如果以鼠标为锚点缩放图片,图片上处于鼠标所在位置的点(通常也是用户关注的图片上的点)不会移动,更不会消失在图片显示区域之外,可以提高用户体验,是一个值得实现的效果。

实现以鼠标所在位置为锚点进行图片缩放的效果的最简单的方法是以QGraphicsView作为图片的容器,只需要在初始化时设置:

self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)

即可实现以鼠标为锚点缩放图片的效果。下面是一个简单示例:

from PyQt5.QtWidgets import (QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QPushButton, QVBoxLayout, QFileDialog, QWidget
)
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt
import sysclass ImageViewer(QGraphicsView):def __init__(self, parent=None):super().__init__(parent)self.scene = QGraphicsScene(self)self.setScene(self.scene)self.scale_factor = 1.0  # 缩放因子self.setDragMode(QGraphicsView.ScrollHandDrag)  # 设置拖动模式self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)  # 设置缩放锚点为鼠标位置def add_image(self, image_path):"""向 QGraphicsView 中添加一张图片"""pixmap = QPixmap(image_path)if pixmap.isNull():return# 创建 QGraphicsPixmapItem 并添加到场景中pixmap_item = self.scene.addPixmap(pixmap)pixmap_item.setTransformationMode(Qt.SmoothTransformation)# 其他图片:可移动、可缩放pixmap_item.setFlags(pixmap_item.flags() | pixmap_item.ItemIsMovable)  # 添加可移动标志def wheelEvent(self, event):"""实现以鼠标位置为锚点的缩放"""# 获取鼠标在视图中的位置# mouse_pos = event.pos()# 将鼠标位置转换为场景坐标# scene_pos = self.mapToScene(mouse_pos)# 根据滚轮方向调整缩放因子if event.angleDelta().y() > 0:scale_change = 1.15  # 放大else:scale_change = 1 / 1.15  # 缩小# 执行缩放,水平方向与垂直方向等比例缩放self.scale(scale_change, scale_change)class MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Image Viewer with Multiple Images")self.setGeometry(100, 100, 800, 600)# 创建主窗口布局central_widget = QWidget()self.setCentralWidget(central_widget)layout = QVBoxLayout(central_widget)# 创建 QGraphicsViewself.graphics_view = ImageViewer(self)layout.addWidget(self.graphics_view)# 创建按钮self.load_button = QPushButton("加载图片")self.load_button.clicked.connect(self.open_file_dialog)layout.addWidget(self.load_button)def open_file_dialog(self):"""打开文件对话框选择图片"""file_path, _ = QFileDialog.getOpenFileName(self, "选择图片", "", "图片文件 (*.png *.jpg *.jpeg *.bmp)")if file_path:self.graphics_view.add_image(file_path)if __name__ == "__main__":app = QApplication(sys.argv)window = MainWindow()# 禁用最大化按钮window.setWindowFlags(window.windowFlags() & ~Qt.WindowMaximizeButtonHint)window.show()sys.exit(app.exec_())

上面的示例包含三个步骤:

1、创建了一个类ImageViewer继承QGraphicsView,在__init__方法中设置以鼠标所在位置为锚点缩放:

# 设置缩放锚点为鼠标位置

self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)  

2、通过QGraphicsPixmapItem加载一张图片,然后将QGraphicsPixmapItem加入场景:

# 创建并设置场景

self.scene = QGraphicsScene(self)

self.setScene(self.scene)

pixmap = QPixmap(image_path)

if pixmap.isNull():

      return

# 创建 QGraphicsPixmapItem 加载图片,并将QGraphicsPixmapItem添加到场景中

pixmap_item = self.scene.addPixmap(pixmap)

3、重写wheelEvent事件实现图片缩放:

def wheelEvent(self, event):

        """实现以鼠标位置为锚点的缩放"""

        # 根据滚轮方向调整缩放因子

        if event.angleDelta().y() > 0:

            scale_change = 1.15  # 放大

        else:

            scale_change = 1 / 1.15  # 缩小

        # 执行缩放,水平方向与垂直方向等比例缩放

        self.scale(scale_change, scale_change)

可以看到,通过以上三个步骤,基本上不用添加与图片位置相关的代码,QGraphicsView就实现了以鼠标所在位置为锚点缩放图片的效果。

但是,上面这种QGraphicsView的实现虽然简单,其实不够精确。将图片不断缩小,会发现缩小到某个程度后图片会脱离鼠标所在位置。那么如何实现精确的以鼠标所在位置为锚点缩放图片的效果呢?

先看以下图示:

首先有一个显示图片的容器,例如QLabel,给这个容器一个固定的尺寸,它有一个以左上角为(0,0)的坐标系。

其次有一张图片,为美观起见,将这张图片(实际上是经过缩放后的图片)水平垂直居中显示在容器中,图片自身拥有一个以图片的左上角为(0,0)的像素坐标系。像素坐标系与容器坐标系的原点存在偏差(img_offset),而图片上所有像素在图片像素坐标系中的坐标与在容器坐标系中的坐标的偏差与两个坐标系原点的偏差是相等的。经过缩放后,鼠标在图片上的位置会发生偏移delta_offset,只需将img_offset调整delta_offset,那么,缩放后的鼠标位置就调整回了缩放前的位置,形成了以鼠标为锚点的缩放效果。应当注意的是,容器响应鼠标事件时,鼠标位置的值是以容器坐标系的数据表示的,而前述调整img_offset的方法是基于图片像素坐标系的,因此要将容器坐标系表示的鼠标位置转换为图片像素坐标系,方法如下(以下代码中img_offset采用了scaled_pixmap_offset的变量名):

mouse_pos_on_scaled_pixmap = event.pos() - self.scaled_pixmap_offset

也就是说,需要在wheelEvent事件中添加一些坐标变换的代码,具体如下:

     def wheelEvent(self, event: QWheelEvent):"""重写滚轮事件,实现缩放"""mouse_pos_on_scaled_pixmap = event.pos() - self.scaled_pixmap_offset# 根据滚轮方向调整缩放因子if event.angleDelta().y() > 0:zoom_factor = 1.15  # 放大else:zoom_factor = 1 / 1.15  # 缩小# 计算累计缩放比例self.scale_factor *= zoom_factor# 缩放图片self.scaled_pixmap = self.original_pixmap.scaled(self.size() * self.scale_factor,Qt.KeepAspectRatio,Qt.SmoothTransformation)# 计算鼠标在图片缩放前后的偏移量delta_offset = mouse_pos_on_scaled_pixmap * (zoom_factor - 1)# 调整图像显示位置对容器左上角的偏移量self.scaled_pixmap_offset -= delta_offset# 更新图像显示self.update()

完整示例如下:

from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget, QFileDialog, QPushButton
from PyQt5.QtGui import QPixmap, QImage, QWheelEvent, QPainter
from PyQt5.QtCore import Qt, QPointFclass ImageLabel(QLabel):def __init__(self, parent=None):super().__init__(parent)self.setAlignment(Qt.AlignCenter)  # 图像居中显示self.scale_factor = 1.0  # 缩放因子self.original_pixmap = None  # 原始图像self.scaled_pixmap = None  # 缩放后的图像self.setFixedSize(600, 600)self.scaled_pixmap_offset = QPointF(0, 0)def set_image(self, image_path):"""加载图像并显示"""self.original_pixmap = QPixmap(image_path)if self.original_pixmap.isNull():returnif self.scaled_pixmap is None:self.scaled_pixmap = self.original_pixmap.scaled(self.size(),Qt.KeepAspectRatio,Qt.SmoothTransformation)self.scaled_pixmap_offset_x = (self.size().width() - self.scaled_pixmap.size().width()) / 2self.scaled_pixmap_offset_y = (self.size().height() - self.scaled_pixmap.size().height()) / 2self.scaled_pixmap_offset = QPointF(self.scaled_pixmap_offset_x, self.scaled_pixmap_offset_y)self.update()def paintEvent(self, event):painter = QPainter(self)if self.scaled_pixmap is not None:painter.drawPixmap(self.scaled_pixmap_offset, self.scaled_pixmap)def wheelEvent(self, event: QWheelEvent):"""重写滚轮事件,实现缩放"""mouse_pos_on_scaled_pixmap = event.pos() - self.scaled_pixmap_offset# 根据滚轮方向调整缩放因子if event.angleDelta().y() > 0:zoom_factor = 1.15  # 放大else:zoom_factor = 1 / 1.15  # 缩小# 计算累计缩放比例self.scale_factor *= zoom_factor# 缩放图片self.scaled_pixmap = self.original_pixmap.scaled(self.size() * self.scale_factor,Qt.KeepAspectRatio,Qt.SmoothTransformation)# 计算鼠标在图片缩放前后的偏移量delta_offset = mouse_pos_on_scaled_pixmap * (zoom_factor - 1)# 调整图像显示位置对容器左上角的偏移量self.scaled_pixmap_offset -= delta_offset# 更新图像显示self.update()class MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Image Viewer with QLabel")self.setGeometry(100, 100, 800, 600)# 创建主窗口布局central_widget = QWidget()self.setCentralWidget(central_widget)layout = QVBoxLayout(central_widget)# 创建 QLabel 用于显示图像self.image_label = ImageLabel(self)layout.addWidget(self.image_label)# 创建按钮self.load_button = QPushButton("加载图片")self.load_button.clicked.connect(self.open_file_dialog)layout.addWidget(self.load_button)def open_file_dialog(self):"""打开文件对话框选择图片"""file_path, _ = QFileDialog.getOpenFileName(self, "选择图片", "", "图片文件 (*.png *.jpg *.jpeg *.bmp)")if file_path:self.image_label.set_image(file_path)if __name__ == "__main__":app = QApplication([])window = MainWindow()# 禁用最大化按钮window.setWindowFlags(window.windowFlags() & ~Qt.WindowMaximizeButtonHint)window.show()app.exec_()

相信经过这两个示例,特别是后一个以QLabel作为图片容器实现的示例,可以完全掌握以鼠标为锚点缩放图片的原理,不但能够将QGraphicsView以鼠标为锚点进行缩放的效果做得更精确,也能在pyqt以外的其他领域运用自如了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/77243.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

巧记英语四级单词 Unit5-中【晓艳老师版】

ignore v.无视,不理睬 发音“一个闹”,对付一个无理取闹的孩子,最好的方式就是无视 不理睬ignorant a.无知的,不礼貌的 对于什么事都无视,中国第一个不平等条约问也不知道就是无知的neglect n.忽视 negative消极的&a…

go 编译的 windows 进程(exe)以管理员权限启动(UAC)

引言 windows 系统,在打开某些 exe 的时候,会弹出“用户账户控制(UAC)”的弹窗 “你要允许来自xx发布者的此应用对你的设备进行更改吗?” UAC(User Account Control,用户账户控制)是 Windows 操作系统中的…

go.mod介绍

在 Go 项目中,.mod 文件(全称 go.mod)是 Go 语言模块(Module)系统的核心配置文件,用于定义和管理项目的依赖关系、模块名称及兼容性规则。以下是其核心作用与结构的详细说明: 一、go.mod 文件的…

基于CATIA参数化管道建模的自动化插件开发实践——NX建模之管道命令的参考与移植

引言 在机械设计领域,CATIA作为行业领先的CAD软件,其强大的参数化建模能力备受青睐。本文介绍如何利用Python的PySide6框架与CATIA二次开发技术,开发一款智能管状体生成工具。该工具借鉴了同类工业软件NX的建模的管道命令,通过Py…

centos7使用yum快速安装最新版本Jenkins-2.462.3

Jenkins支持多种安装方式:yum安装、war包安装、Docker安装等。 官方下载地址:https://www.jenkins.io/zh/download 本次实验使用yum方式安装Jenkins LTS长期支持版,版本为 2.462.3。 一、Jenkins基础环境的安装与配置 1.1:基本…

BiliNote:开源的AI视频笔记生成工具,让知识提取与分享更高效——跨平台自动生成结构化笔记,实现从视频到Markdown的智能转化

引言:视频学习的痛点与BiliNote的解决方案 随着知识视频化趋势的加速,B站、YouTube等平台成为学习与信息获取的重要渠道,但手动记录笔记耗时低效、信息碎片化等问题依然突出。BiliNote的出现,通过AI驱动的自动化流程,将视频内容转化为结构清晰的Markdown笔记,支持截图插…

DAX Studio将PowerBI与EXCEL连接

DAX Studio将PowerBI与EXCEL连接 具体步骤如下: 第一步:先打开一个PowerBI的文件,在外部工具栏里打开DAXStudio,如图: 第二步:DAXStudio界面,点击Advanced选项卡-->Analyze in Excel&#…

Redis-cli常用参数及功能的详细说明

Redis-cli常用参数及功能的详细说明 相关参考知识书籍 <<Redis运维与开发>> 以下是Redis-cli常用参数及功能的详细说明 1. **-r​&#xff08;重复执行命令&#xff09;** 作用&#xff1a;重复执行指定命令多次。 示例&#xff1a;执行3次PING​命令&#xff1…

百度文心4.5 Turbo与DeepSeek、豆包、元宝对比:技术路径与市场格局分析​​

今日&#xff0c;百度发布文心大模型4.5 Turbo与X1 Turbo&#xff0c;主打多模态能力提升与成本优化&#xff0c;成为AI搜索领域的重要技术迭代。与此同时&#xff0c;DeepSeek、豆包&#xff08;字节跳动&#xff09;、腾讯元宝等竞品凭借差异化定位持续抢占市场。本文将从技术…

施工配电箱巡检二维码应用

在过去&#xff0c;施工配电箱的巡检主要依赖于纸质记录方式。巡检人员每次巡检时&#xff0c;都要在纸质表格上详细填写配电箱的各项参数、运行状况以及巡检时间等信息。这种方式在实际操作中暴露出诸多严重问题&#xff0c;信息易出现错误、数据会有造假现象、数据量庞大整理…

国产AI大模型超深度横评:技术参数全解、商业落地全场景拆解

评测方法论与指标体系 评测框架设计 采用三层评估体系&#xff0c;涵盖技术性能、商业价值、社会效益三大维度&#xff0c;细分为12个二级指标、36个三级指标&#xff1a; 测试环境配置 项目配置详情硬件平台8NVIDIA H100集群&#xff0c;NVLink全互联&#xff0c;3TB内存软…

施工安全巡检二维码制作

进入新时代以来&#xff0c;人们对安全的重视程度越来越高。特别在建筑施工行业&#xff0c;安全不仅是关乎着工人的性命&#xff0c;更是承载着工人背后家庭的幸福生活。此时就诞生了安全巡检的工作&#xff0c;而巡检过程中内容庞杂&#xff0c;安全生产检查、隐患排查、施工…

【AI平台】n8n入门3:第二个工作流,链接网上大模型(含三种方式)

前言 n8n是一款开源的低代码自动化工具&#xff0c;专注于AI工作流构建&#xff0c;支持灵活的自定义与集成。 就是可以把大模型和其他工具联合起来&#xff0c;这就厉害了。而且&#xff0c;免费功能又强大&#xff0c;目前很火&#xff0c;来研究一下。 功能说明 本节目标…

【Test】单例模式❗

文章目录 1. 单例模式2. 单例模式简单示例3. 懒汉模式4. 饿汉模式5. 懒汉式和饿汉式的区别 1. 单例模式 &#x1f427;定义&#xff1a;保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点。 单例模式是一种常用的软件设计模式&#xff0c;在它的核心结构中只包…

Kotlin 协程在 LiveData 中的完美封装:CoroutineLiveData 全解

&#x1f300; 什么是 CoroutineLiveData&#xff1f; CoroutineLiveData 是 liveData 构造器创建出来的 LiveData 对象&#xff0c;它是 Jetpack 中为协程量身打造的 LiveData 版本&#xff0c;主要用来让我们在 LiveData 的作用域内&#xff0c;安全、方便地使用协程。 它的…

在 Java 项目中搭建和部署 Docker 的详细流程

引言 在现代软件开发中&#xff0c;Docker 已成为一种流行的工具&#xff0c;用于简化应用的部署和运行环境的一致性。本文将详细介绍如何在 Java 项目中搭建和部署 Docker&#xff0c;包括配置文件、代码示例以及流程图。 一、整体工作流程 以下是整个流程的概览&#xff1a…

阿里云域名迁移至Amazon Route 53的完整指南

在当今的云计算时代,域名管理和DNS服务的选择对于网站性能和可用性至关重要。本文将详细介绍如何将阿里云上的域名迁移至Amazon Route 53,以充分利用AWS生态系统的优势。 1. 简介 Amazon Route 53是一种可用性高、可扩展性强的域名系统(DNS)web服务。它为开发者和企业提供了一…

AI网络渗透kali应用(gptshell)

kali安装gptshell 一、shellGPT 工具介绍 ShellGPT‌是一款由AI大型语言模型&#xff08;LLM&#xff09;驱动的终端命令行工具。它能帮助用户直接在终端与AI交互&#xff0c;自动生成、解释、执行各类 Linux 命令&#xff0c;大大提升了运维和开发效率。ShellGPT 支持接入 O…

STM32 I2C总线通信协议

引言 在嵌入式系统开发领域&#xff0c;I2C&#xff08;Inter-Integrated Circuit&#xff09;总线作为经典的双线制串行通信协议&#xff0c;凭借其简洁的物理层设计和灵活的通信机制&#xff0c;在传感器互联、存储设备控制、显示模块驱动等场景中占据重要地位。本文将深入剖…

spring,spring boot, spring cloud三者区别

Spring Framework vs Spring Boot vs Spring Cloud 1. Spring Framework 定位&#xff1a;基础框架&#xff0c;提供核心的IoC容器、AOP、事务管理、数据访问、Web MVC等能力。特点&#xff1a; 模块化设计&#xff1a;可单独使用某些模块&#xff08;如仅用Spring JDBC&…