Python+PyQt5制作一個(gè)圖片查看器
前言
在 PyQt 中可以使用很多方式實(shí)現(xiàn)照片查看器,最樸素的做法就是重寫 QWidget 的 paintEvent()、mouseMoveEvent 等事件,但是如果要在圖像上多添加一些形狀,那么在對(duì)圖像進(jìn)行縮放旋轉(zhuǎn)等仿射變換時(shí)需要對(duì)這些形狀也這些變換,雖然不難,但是從頭實(shí)現(xiàn)這些變換還有形狀還是挺討厭的。好在 Qt 提供了圖形視圖框架,關(guān)于這個(gè)框架的基本使用可以參見 深入了解PyQt5中的圖形視圖框架,下面進(jìn)入正題。
實(shí)現(xiàn)方式
一個(gè)最基本的照片查看器應(yīng)該具有以下功能:
- 載入圖像
- 縮放圖像
- 在窗口尺寸小于圖像時(shí)允許拖拽圖像
載入圖像可以使用 QGraphicsPixmapItem 來(lái)解決,縮放圖像使用 QGraphicsView 的 scale(sx, sy) 解決,移動(dòng)圖像只需將 QGraphicsView 的 dragMode 設(shè)置為 QGraphicsView.ScrollHandDrag 即可。因?yàn)槌3J褂檬髽?biāo)滾輪來(lái)縮放圖像,所以還需要重寫重寫以下 QGraphicsView 的 wheelEvent。
實(shí)際上由于窗口的縮放導(dǎo)致視口大小變化,還有一些細(xì)枝末節(jié)需要處理。具體代碼如下:
# coding:utf-8
import sys
from PyQt5.QtCore import QRect, QRectF, QSize, Qt
from PyQt5.QtGui import QPainter, QPixmap, QWheelEvent
from PyQt5.QtWidgets import (QApplication, QGraphicsItem, QGraphicsPixmapItem,
QGraphicsScene, QGraphicsView)
class ImageViewer(QGraphicsView):
""" 圖片查看器 """
def __init__(self, parent=None):
super().__init__(parent=parent)
self.zoomInTimes = 0
self.maxZoomInTimes = 22
# 創(chuàng)建場(chǎng)景
self.graphicsScene = QGraphicsScene()
# 圖片
self.pixmap = QPixmap(r'D:\hzz\圖片\硝子\硝子 (2).jpg')
self.pixmapItem = QGraphicsPixmapItem(self.pixmap)
self.displayedImageSize = QSize(0, 0)
# 初始化小部件
self.__initWidget()
def __initWidget(self):
""" 初始化小部件 """
self.resize(1200, 900)
# 隱藏滾動(dòng)條
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
# 以鼠標(biāo)所在位置為錨點(diǎn)進(jìn)行縮放
self.setTransformationAnchor(self.AnchorUnderMouse)
# 平滑縮放
self.pixmapItem.setTransformationMode(Qt.SmoothTransformation)
self.setRenderHints(QPainter.Antialiasing |
QPainter.SmoothPixmapTransform)
# 設(shè)置場(chǎng)景
self.graphicsScene.addItem(self.pixmapItem)
self.setScene(self.graphicsScene)
def wheelEvent(self, e: QWheelEvent):
""" 滾動(dòng)鼠標(biāo)滾輪縮放圖片 """
if e.angleDelta().y() > 0:
self.zoomIn()
else:
self.zoomOut()
def resizeEvent(self, e):
""" 縮放圖片 """
super().resizeEvent(e)
if self.zoomInTimes > 0:
return
# 調(diào)整圖片大小
ratio = self.__getScaleRatio()
self.displayedImageSize = self.pixmap.size()*ratio
if ratio < 1:
self.fitInView(self.pixmapItem, Qt.KeepAspectRatio)
else:
self.resetTransform()
def setImage(self, imagePath: str):
""" 設(shè)置顯示的圖片 """
self.resetTransform()
# 刷新圖片
self.pixmap = QPixmap(imagePath)
self.pixmapItem.setPixmap(self.pixmap)
# 調(diào)整圖片大小
self.setSceneRect(QRectF(self.pixmap.rect()))
ratio = self.__getScaleRatio()
self.displayedImageSize = self.pixmap.size()*ratio
if ratio < 1:
self.fitInView(self.pixmapItem, Qt.KeepAspectRatio)
def resetTransform(self):
""" 重置變換 """
super().resetTransform()
self.zoomInTimes = 0
self.__setDragEnabled(False)
def __isEnableDrag(self):
""" 根據(jù)圖片的尺寸決定是否啟動(dòng)拖拽功能 """
v = self.verticalScrollBar().maximum() > 0
h = self.horizontalScrollBar().maximum() > 0
return v or h
def __setDragEnabled(self, isEnabled: bool):
""" 設(shè)置拖拽是否啟動(dòng) """
self.setDragMode(
self.ScrollHandDrag if isEnabled else self.NoDrag)
def __getScaleRatio(self):
""" 獲取顯示的圖像和原始圖像的縮放比例 """
if self.pixmap.isNull():
return 1
pw = self.pixmap.width()
ph = self.pixmap.height()
rw = min(1, self.width()/pw)
rh = min(1, self.height()/ph)
return min(rw, rh)
def fitInView(self, item: QGraphicsItem, mode=Qt.KeepAspectRatio):
""" 縮放場(chǎng)景使其適應(yīng)窗口大小 """
super().fitInView(item, mode)
self.displayedImageSize = self.__getScaleRatio()*self.pixmap.size()
self.zoomInTimes = 0
def zoomIn(self, viewAnchor=QGraphicsView.AnchorUnderMouse):
""" 放大圖像 """
if self.zoomInTimes == self.maxZoomInTimes:
return
self.setTransformationAnchor(viewAnchor)
self.zoomInTimes += 1
self.scale(1.1, 1.1)
self.__setDragEnabled(self.__isEnableDrag())
# 還原 anchor
self.setTransformationAnchor(self.AnchorUnderMouse)
def zoomOut(self, viewAnchor=QGraphicsView.AnchorUnderMouse):
""" 縮小圖像 """
if self.zoomInTimes == 0 and not self.__isEnableDrag():
return
self.setTransformationAnchor(viewAnchor)
self.zoomInTimes -= 1
# 原始圖像的大小
pw = self.pixmap.width()
ph = self.pixmap.height()
# 實(shí)際顯示的圖像寬度
w = self.displayedImageSize.width()*1.1**self.zoomInTimes
h = self.displayedImageSize.height()*1.1**self.zoomInTimes
if pw > self.width() or ph > self.height():
# 在窗口尺寸小于原始圖像時(shí)禁止繼續(xù)縮小圖像比窗口還小
if w <= self.width() and h <= self.height():
self.fitInView(self.pixmapItem)
else:
self.scale(1/1.1, 1/1.1)
else:
# 在窗口尺寸大于圖像時(shí)不允許縮小的比原始圖像小
if w <= pw:
self.resetTransform()
else:
self.scale(1/1.1, 1/1.1)
self.__setDragEnabled(self.__isEnableDrag())
# 還原 anchor
self.setTransformationAnchor(self.AnchorUnderMouse)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = ImageViewer()
w.show()
sys.exit(app.exec_())
測(cè)試
來(lái)看一下實(shí)際的使用效果:

到此這篇關(guān)于Python+PyQt5制作一個(gè)圖片查看器的文章就介紹到這了,更多相關(guān)PyQt5圖片查看器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python中flatten( )函數(shù)及函數(shù)用法詳解
flatten是numpy.ndarray.flatten的一個(gè)函數(shù),即返回一個(gè)一維數(shù)組。這篇文章主要介紹了Python中flatten( )函數(shù),需要的朋友可以參考下2018-11-11
TensorFlow基于MNIST數(shù)據(jù)集實(shí)現(xiàn)車牌識(shí)別(初步演示版)
這篇文章主要介紹了TensorFlow基于MNIST數(shù)據(jù)集實(shí)現(xiàn)車牌識(shí)別(初步演示版),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
PyTorch中Tensor的維度變換實(shí)現(xiàn)
這篇文章主要介紹了PyTorch中Tensor的維度變換實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Python 通過(guò)監(jiān)聽端口實(shí)現(xiàn)唯一腳本運(yùn)行方式
這篇文章主要介紹了Python 通過(guò)監(jiān)聽端口實(shí)現(xiàn)唯一腳本運(yùn)行方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-05-05
Tensorflow 定義變量,函數(shù),數(shù)值計(jì)算等名字的更新方式
今天小編就為大家分享一篇Tensorflow 定義變量,函數(shù),數(shù)值計(jì)算等名字的更新方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-02-02
python框架flask入門之環(huán)境搭建及開啟調(diào)試
這篇文章主要介紹了python框架flask入門環(huán)境搭建及開啟調(diào)試的步驟設(shè)置,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06

