Python+PyQt5制作一个图片查看器
作者:之一Yo 发布时间:2021-03-03 04:16:06
标签:Python,PyQt5,图片,查看
前言
在 PyQt 中可以使用很多方式实现照片查看器,最朴素的做法就是重写 QWidget
的 paintEvent()
、mouseMoveEvent
等事件,但是如果要在图像上多添加一些形状,那么在对图像进行缩放旋转等仿射变换时需要对这些形状也这些变换,虽然不难,但是从头实现这些变换还有形状还是挺讨厌的。好在 Qt 提供了图形视图框架,关于这个框架的基本使用可以参见 深入了解PyQt5中的图形视图框架,下面进入正题。
实现方式
一个最基本的照片查看器应该具有以下功能:
载入图像
缩放图像
在窗口尺寸小于图像时允许拖拽图像
载入图像可以使用 QGraphicsPixmapItem
来解决,缩放图像使用 QGraphicsView
的 scale(sx, sy)
解决,移动图像只需将 QGraphicsView
的 dragMode
设置为 QGraphicsView.ScrollHandDrag
即可。因为常常使用鼠标滚轮来缩放图像,所以还需要重写重写以下 QGraphicsView
的 wheelEvent
。
实际上由于窗口的缩放导致视口大小变化,还有一些细枝末节需要处理。具体代码如下:
# 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
# 创建场景
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)
# 以鼠标所在位置为锚点进行缩放
self.setTransformationAnchor(self.AnchorUnderMouse)
# 平滑缩放
self.pixmapItem.setTransformationMode(Qt.SmoothTransformation)
self.setRenderHints(QPainter.Antialiasing |
QPainter.SmoothPixmapTransform)
# 设置场景
self.graphicsScene.addItem(self.pixmapItem)
self.setScene(self.graphicsScene)
def wheelEvent(self, e: QWheelEvent):
""" 滚动鼠标滚轮缩放图片 """
if e.angleDelta().y() > 0:
self.zoomIn()
else:
self.zoomOut()
def resizeEvent(self, e):
""" 缩放图片 """
super().resizeEvent(e)
if self.zoomInTimes > 0:
return
# 调整图片大小
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):
""" 设置显示的图片 """
self.resetTransform()
# 刷新图片
self.pixmap = QPixmap(imagePath)
self.pixmapItem.setPixmap(self.pixmap)
# 调整图片大小
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):
""" 根据图片的尺寸决定是否启动拖拽功能 """
v = self.verticalScrollBar().maximum() > 0
h = self.horizontalScrollBar().maximum() > 0
return v or h
def __setDragEnabled(self, isEnabled: bool):
""" 设置拖拽是否启动 """
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):
""" 缩放场景使其适应窗口大小 """
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():
# 在窗口尺寸小于原始图像时禁止继续缩小图像比窗口还小
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_())
测试
来看一下实际的使用效果:
来源:https://www.cnblogs.com/zhiyiYo/p/15676079.html
0
投稿
猜你喜欢
- 游戏规则用pygame动画实现神庙逃亡类似的小游戏,当玩家移动的时候躲避 * ,如果 * 命中玩家或者名字龙都会减速,玩家躲避 * 使更多的 * 打
- 首先呢,需要有两个mysql服务器。如果做测试的话可以在同一台机器上装两个mysql服务程序,注意要两个运行程序的端口不能一样。我用的是一个
- 很多朋友希望,我能把我做网站的一些流程及经验跟大家分享一下,最近刚好做一次内部培训,所以稍微整理了一下,这些只是针对网页初学者,具有一定平面
- 语法:CREATE TRIGGER trigger_name trigger_time trigger_eventON tbl_name F
- 我们使用的是QWebview模块,这里也主要是展示下QWebview的用法。之前在网上找了半天的解析网页的内容,都不是很清楚。这是核心代码:
- 本文介绍Python实现端口复用实例如下所示:#coding=utf-8import socketimport sysimport sele
- SQL Server 2005的新功能为动态管理对象,它们是在指定时间返回某个数据库实例的特殊状态信息的数据库视图或函数。这些对象允许数据库
- 前言在算face_track_id map有感:开始验证data={'state':[1,1,2,2,1,2,2,2],
- 密码学俱乐部的第一条规则是:永远不要自己发明密码系统。密码学俱乐部的第二条规则是:永远不要自己实现密码系统:在现实世界中,在实现以及设计密码
- 现在网页设计师除了把页面做的漂亮以外,越来越注重“用户体验”,就是要做“别让用户思考”的网页,使网站真正做到“可用性”。望望结合几年的工作经
- 如何实现优惠打折? 代码及说明见下:<%@ LANG
- wordcloud是Python扩展库中一种将词语用图片表达出来的一种形式,通过词云生成的图片,我们可以更加直观的看出某篇文章的故事梗概。首
- 使用WSH调用系统的Ping命令,将Ping的结果重定向到一个文本文件中去,再把文本文件显示到网页中具体做法如下:首先, 建一个.BAT文件
- 昨天在网上看到一个防采集软件,说采集只访问当前网页,不会访问网页的图片、JS等,今天突然想到,通过动态程序和Js访问分别记录访问者的IP,然
- LRU缓存算法,指的是近期最少使用算法,大体逻辑就是淘汰最长时间没有用的那个缓存,这里我们使用有序字典,来实现自己的LRU缓存算法,并将其包
- 一、前言前几天,在写一个与差分隐私相关的简单程序时,我发现了一些奇怪的东西:相对于其他的随机数生成函数,Python的random.rand
- 今天发现sympy依赖的库mpmath里也有很多数学函数,其中也有在复平面绘制二维图的函数cplot,具体例子如下from mpmath i
- 首先画出流程图,流程图与现实代码有出入,因为刚开始画流程图的时候,有些东西没考虑进去,后来写着写着就慢慢能想起来并实现了。另有一点经验推荐给
- ORA-00600:internal error code,arguments:[num],[?],[?],[?],[?]产生原因:这种错误
- 一、发送消息import smtplibfrom email.mime.text import MIMETextfrom email.hea