Python+PyQt5制作一個圖片查看器
前言
在 PyQt 中可以使用很多方式實現(xiàn)照片查看器,最樸素的做法就是重寫 QWidget
的 paintEvent()
、mouseMoveEvent
等事件,但是如果要在圖像上多添加一些形狀,那么在對圖像進行縮放旋轉(zhuǎn)等仿射變換時需要對這些形狀也這些變換,雖然不難,但是從頭實現(xiàn)這些變換還有形狀還是挺討厭的。好在 Qt 提供了圖形視圖框架,關(guān)于這個框架的基本使用可以參見 深入了解PyQt5中的圖形視圖框架,下面進入正題。
實現(xiàn)方式
一個最基本的照片查看器應(yīng)該具有以下功能:
- 載入圖像
- 縮放圖像
- 在窗口尺寸小于圖像時允許拖拽圖像
載入圖像可以使用 QGraphicsPixmapItem
來解決,縮放圖像使用 QGraphicsView
的 scale(sx, sy)
解決,移動圖像只需將 QGraphicsView
的 dragMode
設(shè)置為 QGraphicsView.ScrollHandDrag
即可。因為常常使用鼠標(biāo)滾輪來縮放圖像,所以還需要重寫重寫以下 QGraphicsView
的 wheelEvent
。
實際上由于窗口的縮放導(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)建場景 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) # 隱藏滾動條 self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # 以鼠標(biāo)所在位置為錨點進行縮放 self.setTransformationAnchor(self.AnchorUnderMouse) # 平滑縮放 self.pixmapItem.setTransformationMode(Qt.SmoothTransformation) self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) # 設(shè)置場景 self.graphicsScene.addItem(self.pixmapItem) self.setScene(self.graphicsScene) def wheelEvent(self, e: QWheelEvent): """ 滾動鼠標(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ù)圖片的尺寸決定是否啟動拖拽功能 """ v = self.verticalScrollBar().maximum() > 0 h = self.horizontalScrollBar().maximum() > 0 return v or h def __setDragEnabled(self, isEnabled: bool): """ 設(shè)置拖拽是否啟動 """ 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): """ 縮放場景使其適應(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() # 實際顯示的圖像寬度 w = self.displayedImageSize.width()*1.1**self.zoomInTimes h = self.displayedImageSize.height()*1.1**self.zoomInTimes if pw > self.width() or ph > self.height(): # 在窗口尺寸小于原始圖像時禁止繼續(xù)縮小圖像比窗口還小 if w <= self.width() and h <= self.height(): self.fitInView(self.pixmapItem) else: self.scale(1/1.1, 1/1.1) else: # 在窗口尺寸大于圖像時不允許縮小的比原始圖像小 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_())
測試
來看一下實際的使用效果:
到此這篇關(guān)于Python+PyQt5制作一個圖片查看器的文章就介紹到這了,更多相關(guān)PyQt5圖片查看器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 去除二維數(shù)組/二維列表中的重復(fù)行方法
今天小編就為大家分享一篇python 去除二維數(shù)組/二維列表中的重復(fù)行方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01python 獲取sqlite3數(shù)據(jù)庫的表名和表字段名的實例
今天小編就為大家分享一篇python 獲取sqlite3數(shù)據(jù)庫的表名和表字段名的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07超詳細(xì)注釋之OpenCV按位AND OR XOR和NOT
這篇文章主要介紹了OpenCV按位AND OR XOR和NOT運算,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09python range()函數(shù)取反序遍歷sequence的方法
今天小編就為大家分享一篇python range()函數(shù)取反序遍歷sequence的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06Python中定時任務(wù)框架APScheduler的快速入門指南
APScheduler是基于Quartz的一個Python定時任務(wù)框架,實現(xiàn)了Quartz的所有功能,使用起來十分方便。下面這篇文章主要跟大家介紹了Python中定時任務(wù)框架APScheduler的快速入門指南,需要的朋友可以參考借鑒,下面來一起看看吧。2017-07-07