Qt小组件 - 8 图片浏览器

一个自制的图片浏览器,如果不想安装qfluentwidgetsCommandBarView可以使用QWidget+QPushButton替代

安装 qfluentwidgets

pip install PySide6-Fluent-Widgets[full]

代码示例

# coding: utf-8
from typing import Unionfrom PySide6.QtCore import Qt, QRectF, QSize, Signal, QPropertyAnimation, QEasingCurve, Property
from PySide6.QtGui import QPainter, QPixmap, QWheelEvent, QResizeEvent, QImage
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QGraphicsItem, QFileDialog, \QVBoxLayout
from qfluentwidgets import FluentIcon as FIF, InfoBar, CommandBarView, MaskDialogBase, Actionfrom common import imageRequestclass PictureBrowserView(QGraphicsView):"""图片查看器"""closeSignal = Signal(bool)def __init__(self, parent=None):super().__init__(parent)self._rotationAngle = 0.0self.zoomInTimes = 0self.maxZoomInTimes = 22self.displayedImageSize = QSize(0, 0)self.verticalLayout = QVBoxLayout(self)self.toolsBar = CommandBarView(self)self.graphicsScene = QGraphicsScene(self)self.pixmapItem = QGraphicsPixmapItem()self.__animation = QPropertyAnimation(self, b'rotation', self)self.__animation.setDuration(100)self.__animation.setEasingCurve(QEasingCurve.Type.InOutQuart)self.__animation.finished.connect(self.setAdaptation)self.__initWidgets()self.__initSignals()def __initWidgets(self):self.toolsBar.move(0, 0)self.toolsBar.resize(self.width(), 50)self.setAlignment(Qt.AlignmentFlag.AlignCenter)self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)  # 隐藏水平滚动条self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)  # 隐藏垂直滚动条self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)  # 以鼠标所在位置为锚点进行缩放self.pixmapItem.setTransformationMode(Qt.TransformationMode.SmoothTransformation)  # 平滑转型self.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.LosslessImageRendering | QPainter.RenderHint.SmoothPixmapTransform)  # 平滑像素图变换self.setContentsMargins(0, 0, 0, 0)self.setViewportMargins(0, 0, 0, 0)# 设置布局self.verticalLayout.setContentsMargins(10, 10, 10, 10)self.verticalLayout.setSpacing(0)self.verticalLayout.addWidget(self.toolsBar, 0, Qt.AlignTop | Qt.AlignRight)# 添加图片标签self.graphicsScene.addItem(self.pixmapItem)self.setScene(self.graphicsScene)# 设置控件样式self.setStyleSheet('QGraphicsView{background-color: transparent; border: none;}')def __initSignals(self):self.toolsBar.addAction(Action(FIF.ROTATE, '旋转图片', triggered=lambda: self.setRotationByAnimation()))self.toolsBar.addAction(Action(FIF.ZOOM_IN, '放大图片', triggered=lambda: self.enlargePicture()))self.toolsBar.addAction(Action(FIF.ZOOM_OUT, '缩小图片', triggered=lambda: self.shrinkPicture()))self.toolsBar.addAction(Action(FIF.SAVE, '保存图片', triggered=self.__saveImage))self.toolsBar.addAction(Action(FIF.CLOSE, '关闭窗口', triggered=self.closeSignal.emit))self.toolsBar.resizeToSuitableWidth()def __saveImage(self):fileName, t = QFileDialog.getSaveFileName(self, self.tr("保存图片"), "", self.tr("图片 (*.png)"))if fileName:self.pixmapItem.pixmap().save(fileName, 'PNG')InfoBar.success('', self.tr("图片已保存到") + fileName, self.window())def __getScaleRatio(self):"""获取显示的图像和原始图像的缩放比例:return:"""pm = self.pixmapItem.pixmap()if pm.isNull():return 1pw = pm.width()ph = pm.height()rw = min(1, self.width() / pw)rh = min(1, self.height() / ph)return min(rw, rh)def __setDragEnabled(self, isEnabled: bool):"""设置拖拽是否启动:param isEnabled: bool:return:"""if isEnabled:self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)else:self.setDragMode(QGraphicsView.DragMode.NoDrag)def __isEnableDrag(self):"""根据图片的尺寸决定是否启动拖拽功能:return:"""v = self.verticalScrollBar().maximum() > 0h = self.horizontalScrollBar().maximum() > 0return v or hdef getRotation(self) -> float:return self.pixmapItem.rotation()def setRotation(self, stepSize: Union[float, int] = 90.0):"""顺时针旋转:param stepSize: 步长,旋转角度:return:"""if self.pixmapItem.pixmap().isNull():return# self.pixmapItem.setTransformOriginPoint(self.pixmapItem.boundingRect().center())  # 指定图片旋转中心点self.pixmapItem.setRotation(stepSize)def setRotationByAnimation(self, stepSize: int = 90):"""顺时针旋转动画:param stepSize: 步长,旋转角度:return:"""if self.__animation.state() == QPropertyAnimation.State.Running:returnself.__animation.setStartValue(self._rotationAngle)self._rotationAngle += stepSizeself.__animation.setEndValue(self._rotationAngle)self.__animation.start()def resetTransform(self):"""重置变换:return:"""self.zoomInTimes = 0self.__setDragEnabled(False)super().resetTransform()def setAdaptation(self):"""缩放以适应:return:"""self.setSceneRect(QRectF(self.pixmapItem.pixmap().rect()))self.fitInView(self.pixmapItem)self.__setDragEnabled(False)self.zoomInTimes = 0def setOriginalSize(self):"""设置 1:1 大小:return:"""self.resetTransform()self.setSceneRect(QRectF(self.pixmapItem.pixmap().rect()))self.__setDragEnabled(self.__isEnableDrag())self.zoomInTimes = self.getZoomInTimes(self.pixmapItem.pixmap().width())def enlargePicture(self, anchor=QGraphicsView.ViewportAnchor.AnchorUnderMouse):"""放大图片:return:"""if self.zoomInTimes == self.maxZoomInTimes:returnself.setTransformationAnchor(anchor)self.zoomInTimes += 1self.scale(1.1, 1.1)self.__setDragEnabled(self.__isEnableDrag())# 还原 anchorself.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)def shrinkPicture(self, anchor=QGraphicsView.ViewportAnchor.AnchorUnderMouse):"""缩小图片:return:"""if self.zoomInTimes == 0 and not self.__isEnableDrag():returnself.setTransformationAnchor(anchor)self.zoomInTimes -= 1# 原始图像的大小pm = self.pixmapItem.pixmap()pw = pm.width()ph = pm.height()# 实际显示的图像宽度w = self.displayedImageSize.width() * 1.1 ** self.zoomInTimesh = self.displayedImageSize.height() * 1.1 ** self.zoomInTimesif 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())# 还原 anchorself.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)def getZoomInTimes(self, width: int, step: int = 100):for i in range(0, self.maxZoomInTimes):if width - self.displayedImageSize.width() * 1.1 ** i <= step:return ireturn self.maxZoomInTimesdef fitInView(self, item: QGraphicsItem, mode=Qt.AspectRatioMode.KeepAspectRatio):"""缩放场景使其适应窗口大小:param item::param mode::return:"""super().fitInView(item, mode)self.displayedImageSize = self.__getScaleRatio() * self.pixmapItem.pixmap().size()self.zoomInTimes = 0def resizeEvent(self, event: QResizeEvent):"""重写 resizeEvent 事件,调整图片大小:param event::return:"""# super().resizeEvent(event)if self.zoomInTimes > 0:return# 调整图片大小ratio = self.__getScaleRatio()self.displayedImageSize = self.pixmapItem.pixmap().size() * ratioif ratio < 1:self.fitInView(self.pixmapItem)else:self.resetTransform()def wheelEvent(self, e: QWheelEvent):"""滚动鼠标滚轮缩放图片:param e::return:"""if e.angleDelta().y() > 0:self.enlargePicture()else:self.shrinkPicture()def setPixmap(self, pixmap: Union[str, QPixmap, QImage]):"""设置图片:param pixmap::return:"""if not pixmap:returnif isinstance(pixmap, str):pixmap = QPixmap(pixmap)elif isinstance(pixmap, QImage):pixmap = QPixmap.fromImage(pixmap)# 设置图片, 并设置场景大小self.pixmapItem.setPixmap(pixmap)# 缩放图片ratio = self.__getScaleRatio()self.displayedImageSize = pixmap.size() * ratioself.pixmapItem.setTransformOriginPoint(self.pixmapItem.boundingRect().center())self.setSceneRect(QRectF(pixmap.rect()))self.setAdaptation()def setUrl(self, url: str):imageRequest(self, url)rotation = Property(float, getRotation, setRotation)class PictureBrowserDialog(MaskDialogBase):"""图片浏览器"""def __init__(self, parent=None):super().__init__(parent)self.vBoxLayout = QVBoxLayout(self.widget)self.view = PictureBrowserView(self.widget)self.view.closeSignal.connect(self.close)self.vBoxLayout.setContentsMargins(0, 0, 0, 0)self._hBoxLayout.setContentsMargins(0, 0, 0, 0)self.vBoxLayout.addWidget(self.view)self.setClosableOnMaskClicked(True)def setUrl(self, url: str):self.view.setUrl(url)
main.py

QImageQPixmap不太支持大文件浏览,或者未知图片类型,使用pillow嵌套使用

# coding: utf-8
import sys
from PIL import Image
from PIL.ImageQt import toqpixmap
from PySide6.QtWidgets import QApplicationfrom components import PictureBrowserViewapp = QApplication(sys.argv)
view = PictureBrowserView()
# view.setPixmap(r"G:\手机\壁纸\20250616153644.png")
view.setPixmap(toqpixmap(Image.open(r"G:\手机\壁纸\0250616153644.png")))
view.show()
sys.exit(app.exec())

在这里插入图片描述

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

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

相关文章

R study notes[1]

文章目录introducing to Rreferencesintroducing to R R is an integrated suite involved data handling,storage facility,calculations on arrays,tools for data analysis and so on.running the command R in the terminal of OS can start R software.in R terminal ,to…

由于主库切换归档路径导致的 Oracle DG 无法同步问题的解决过程

由于主库切换归档路径导致的 Oracle DG 无法同步问题的解决过程 在上一篇文章中&#xff0c;由于 Oracle 数据库的归档日志空间耗尽导致客户端无法连接数据库。在解决的过程中临时修改了归档路径。后来通过修改参数db_recovery_file_dest_size的值解决了问题。 但该操作导致DG无…

密码学与加密货币:构建去中心化信任的技术基石与未来挑战

密码学是加密货币的技术基石&#xff0c;两者通过数学原理构建去中心化信任体系。以下从技术原理、应用场景及未来挑战三方面展开分析&#xff1a;一、密码学基础&#xff1a;加密货币的安全基石非对称加密体系公钥与私钥&#xff1a;基于椭圆曲线密码学&#xff08;ECC&#x…

用于 Web 认证的 抗量子签名——ML-DSA 草案

1. 引言 本文描述了在 Web Authentication (WebAuthn) 中实现无密码认证&#xff08;Passwordless authentication&#xff09;的方法&#xff0c;该方法使用模块格&#xff08;Module-Lattice&#xff09;为基础的数字签名标准&#xff08;ML-DSA&#xff09;&#xff0c;即 …

ubuntu18.04解压大的tar.gz文件失败

1. 问题描述 我在vmware的虚拟机装有petalinux环境&#xff0c;需要解压downloads_2020.2.tar.gz这个大的压缩包文件&#xff0c;但是总是失败&#xff0c;而且过程很漫长 tar: downloads/git2/github.com.vim.vim.git/objects/pack/pack-f7f2e2add0c8972a9141b557ef725c38069…

App拉起:唤醒即达,告别繁琐操作

在移动互联网进入存量竞争的今天&#xff0c;“让用户少点一次、少等一秒”往往意味着20%以上的转化率差异。openinstall把这套体验总结成一套可落地的App拉起方案&#xff1a;一套SDK一组链接跳转规则一个可自定义的落地页&#xff0c;就能把Web→App的整条动线缩成一次点击。…

开发指南125-HTML DOM事件

1、onload和onunload在页面或某个元素加载完成后或离开后触发事件。2、onchange用于在元素的值发生变化时触发事件。一般用于<input>, <select>, <textarea>等元素3、onfocus 和 onblur激活或失去焦点时触发4、onmouseover 和 onmouseout鼠标移入或移除时触发…

使用redis 作为消息队列时, 如何保证消息的可靠性

使用Redis作为消息队列时&#xff0c;如何保证消息的可靠性 在分布式系统中&#xff0c;消息队列扮演着不可或缺的角色&#xff0c;它能够有效地实现服务间的解耦和异步通信。Redis凭借其出色的性能&#xff0c;常常被用作轻量级的消息队列。然而&#xff0c;Redis本质上是一个…

CentOS7 安装和配置教程

CentOS7 安装和配置教程第一部分&#xff1a;安装准备1. 下载CentOS 7镜像2. 创建安装介质第二部分&#xff1a;安装步骤1. 在VMeare上安装CentOS-7-x86_64-Minimal2. 安装配置3. 安装过程第三部分&#xff1a;初始配置1. 首次启动设置2. 网络配置3. 防火墙配置第四部分&#x…

clock_getres系统调用及示例

39. clock_getres - 获取时钟精度 函数介绍 clock_getres系统调用用于获取指定时钟的精度&#xff08;分辨率&#xff09;。它返回时钟能够表示的最小时间间隔。 函数原型 #include <time.h>int clock_getres(clockid_t clk_id, struct timespec *res);功能 获取指定时钟…

MCU+RTOS调试

1. 引言在做项目时&#xff0c;百分之三十的时间写代码&#xff0c;还有百分之70的时间用于调试。本期将以Keil为例进行调试章节的讲解&#xff0c;目的在于做出一个标准化的调试步骤&#xff0c;方便大家学习如何调试代码。内容分为基础调试、中级调试及进阶调试三部分&#x…

Redis的数据淘汰策略是什么?有哪些?

1.监测设置了TTL的数据volatile-lru&#xff1a;淘汰最近最少使用的数据volatile-lfu&#xff1a;淘汰最近使用次数最少的数据volatile-ttl&#xff1b;淘汰将要过期的数据volatile-random&#xff1a;随机淘汰2.监测全库数据allkeys-lru&#xff1a;淘汰最近最少使用的数据all…

相控阵波束跃度指向误差Matlab仿真

波束跃度影响&#xff1a;TR芯片移相器位数、阵元数量、校准后阵元初始相位、TR芯片移相器精度、波控计算精度等。用MATLAB进行TR芯片移相器位数、阵元数量对指向误差进行仿真。 close all; %线阵波束跃度仿真 20250726 %beam displacement % 波束跃度影响&#xff1a;TR芯片移…

板凳-------Mysql cookbook学习 (十二--------6)

MySQL 8 导入二进制文件(trailer.ogv)操作指南 在MySQL中导入二进制文件(如trailer.ogv视频文件)通常有几种方法&#xff0c;我将详细介绍每种方法的操作步骤。 方法一&#xff1a;使用LOAD_FILE函数导入BLOB字段 这是最直接的方法&#xff0c;适合中小型二进制文件。sql - 1. …

昇思学习营-【模型推理和性能优化】学习心得_20250730

一、权重的加载 模型包含两部分&#xff1a; base model 和 LoRA adapter 其中base model的权重在微调时被冻结&#xff0c; 推理时加载原权重即可&#xff0c;LoRA adapter可通过PeftModel.from_pretrained进行加载。 二、启动推理 通过model.generate&#xff0c;启动推理…

[AI8051U入门第十一步]W5500-服务端

学习目标: 1、连接TCP/IP 2、学习W5500作为服务端代码一、TCP/IP介绍 TCP/IP 协议栈介绍 TCP/IP(Transmission Control Protocol / Internet Protocol)是互联网通信的核心协议族,定义了数据如何在网络中进行传输和路由。它由多个协议组成,采用分层架构,确保不同设备之间…

C 标准库 <time.h> 函数详解

目录 概述 1 核心数据类型 1.1 time_t 1.2 clock_t 1.3 struct tm 1.4 size_t 2 核心函数 2.1 时间获取函数 2.2 时间转换函数 2.3 时间差计算 2.4 时间格式化函数 3 线程安全版本&#xff08;POSIX 扩展&#xff09; 3.1 函数列表 3.2 时间处理完整示例 4 重要…

基于BEKK-GARCH模型的参数估计、最大似然估计以及参数标准误估计的MATLAB实现

基于BEKK-GARCH模型的参数估计、最大似然估计以及参数标准误估计的MATLAB实现。BEKK-GARCH模型是一种多变量GARCH模型&#xff0c;用于估计多个时间序列的条件方差和协方差矩阵。 MATLAB实现BEKK-GARCH模型 1. 准备数据 假设你已经有一个时间序列数据矩阵 returns&#xff0c;每…

TDengine 中 TDgpt 用于异常检测

介绍 TDgpt 内置时序数据异常检测模型 TDengine 中定义了异常&#xff08;状态&#xff09;窗口来提供异常检测服务。异常窗口可以视为一种特殊的事件窗口&#xff08;Event Window&#xff09;&#xff0c;即异常检测算法确定的连续异常时间序列数据所在的时间窗口。与普通事件…

统计学08:概率分布

一、随机变量随机变量是一个将 随机事件 映射到 数值 的数学函数&#xff0c;用于描述事件的结果。随机变量可以是离散的&#xff08;如骰子&#xff09;或连续的&#xff08;如人的身高、体重&#xff09;。1&#xff09;概率质量函数PMF——离散随机变量P(X x) 对应于某个值…