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.pswp.cn/web/77243.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/77243.shtml
英文地址,請注明出處:http://en.pswp.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&…