PyQt5图片浏览器

PyQt5图片浏览器

  • 实现方式
    • 功能实现
    • 具体代码
  • 界面实现
    • `pillow`源码修改
    • `ImageQt`错误
    • 主页面布局
  • 项目开源地址

分享一个图片浏览器

实现方式

qt本身有一个QGraphicsView类用来当做视图框架。
具体参考:如何在pyqt中使用 QGraphicsView 实现图片查看器
不过大佬给的例子,功能有点少,因此需要添加一下其他的功能

功能实现

  1. 图片旋转(顺时针/逆时针)
  2. 设置 1:1 大小
  3. 缩放以适应

代码示例:

    def setOriginalSize(self):
        """
        设置 1:1 大小
        :return:
        """
        self.resetTransform()
        self.setSceneRect(QRectF(self.pixmap.rect()))
        self.__setDragEnabled(self.__isEnableDrag())
        self.zoomInTimes = self.getZoomInTimes(self.pixmap.width())
    def setAdaptation(self):
        """
        缩放以适应
        :return:
        """
        self.setSceneRect(QRectF(self.pixmap.rect()))
        self.fitInView(self.pixmapItem, Qt.KeepAspectRatio)
        self.__setDragEnabled(False)
        self.zoomInTimes = 0

    def rotationAngle(self):
        return self._rotationAngle

    def rotateClockwise(self, stepSize: int = 90):
        """
        顺时针旋转
        :param stepSize: 步长,旋转角度
        :return:
        """
        if self.pixmap.fileName() is None:
            return
        self._rotationAngle = self._rotationAngle + stepSize
        self.__rotation(self._rotationAngle)

    def __rotation(self, stepSize: int):
        """
        指定图片中心并旋转
        :return:
        """
        self.pixmapItem.setTransformOriginPoint(self.pixmapItem.boundingRect().center())  # 指定图片旋转中心点
        self.pixmapItem.setRotation(stepSize)
        self.setAdaptation()

具体代码

# coding:utf-8
from PyQt5.QtCore import QRectF, QSize, Qt
from PyQt5.QtGui import QPainter, QPixmap, QWheelEvent, QResizeEvent
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsPixmapItem, QGraphicsItem


class Pixmap(QPixmap):
    def __init__(self, fileName: str, *args, **kwargs):
        super().__init__(fileName, *args, **kwargs)
        self._fileName = fileName

    def fileName(self):
        return self._fileName


class ImageGraphicsView(QGraphicsView):
    """
    图片查看器
    """

    def __init__(self, fileName: str = None, parent=None):
        super().__init__(parent)
        self._rotationAngle = 0

        self.zoomInTimes = 0
        self.maxZoomInTimes = 22
        self.pixmap = Pixmap(fileName)
        self.pixmapItem = QGraphicsPixmapItem()
        self.graphicsScene = QGraphicsScene()
        self.displayedImageSize = QSize(0, 0)

        self.__initWidget()

    def __initWidget(self):
        """
        初始化小部件
        :return:
        """
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)  # 隐藏水平滚动条
        self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)  # 隐藏垂直滚动条
        self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)  # 以鼠标所在位置为锚点进行缩放
        self.pixmapItem.setTransformationMode(Qt.TransformationMode.SmoothTransformation)  # 平滑转型
        self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)  # 平滑像素图变换

        self.pixmapItem.setPixmap(self.pixmap)
        self.graphicsScene.addItem(self.pixmapItem)
        self.setScene(self.graphicsScene)

        self.setStyleSheet('background-color: #ffffff;')

    def setImage(self, fileName: str):
        """
        设置显示的图片
        :param fileName:
        :return:
        """
        self.resetTransform()
        del self.pixmap
        self.pixmap = Pixmap(fileName)
        self.pixmapItem.setPixmap(self.pixmap)
        self.zoomInTimes = 0
        # 调整图片大小
        self.setSceneRect(QRectF(self.pixmap.rect()))
        ratio = self.__getScaleRatio()
        self.displayedImageSize = self.pixmap.size() * ratio
        if ratio < 1:
            self.fitInView(self.pixmapItem, Qt.KeepAspectRatio)
        self.pixmapItem.setTransformOriginPoint(self.pixmapItem.boundingRect().center())

    def setOriginalSize(self):
        """
        设置 1:1 大小
        :return:
        """
        self.resetTransform()
        self.setSceneRect(QRectF(self.pixmap.rect()))
        self.__setDragEnabled(self.__isEnableDrag())
        self.zoomInTimes = self.getZoomInTimes(self.pixmap.width())

    def setAdaptation(self):
        """
        缩放以适应
        :return:
        """
        self.setSceneRect(QRectF(self.pixmap.rect()))
        self.fitInView(self.pixmapItem, Qt.KeepAspectRatio)
        self.__setDragEnabled(False)
        self.zoomInTimes = 0

    def rotationAngle(self):
        return self._rotationAngle

    def rotateClockwise(self, stepSize: int = 90):
        """
        顺时针旋转
        :param stepSize: 步长,旋转角度
        :return:
        """
        if self.pixmap.fileName() is None:
            return
        self._rotationAngle = self._rotationAngle + stepSize
        self.__rotation(self._rotationAngle)

    def __rotation(self, stepSize: int):
        """
        指定图片中心并旋转
        :return:
        """
        self.pixmapItem.setTransformOriginPoint(self.pixmapItem.boundingRect().center())  # 指定图片旋转中心点
        self.pixmapItem.setRotation(stepSize)
        self.setAdaptation()

    def __isEnableDrag(self):
        """
        根据图片的尺寸决定是否启动拖拽功能
        :return:
        """
        v = self.verticalScrollBar().maximum() > 0
        h = self.horizontalScrollBar().maximum() > 0
        return v or h

    def __setDragEnabled(self, isEnabled: bool):
        """
        设置拖拽是否启动
        :param isEnabled: bool
        :return:
        """
        if isEnabled:
            self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
        else:
            self.setDragMode(QGraphicsView.DragMode.NoDrag)

    def __getScaleRatio(self):
        """
        获取显示的图像和原始图像的缩放比例
        :return:
        """
        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 enlargePicture(self, anchor=QGraphicsView.AnchorUnderMouse):
        """
        放大图片
        :return:
        """
        if self.zoomInTimes == self.maxZoomInTimes:
            return
        self.setTransformationAnchor(anchor)
        self.zoomInTimes += 1
        self.scale(1.1, 1.1)
        self.__setDragEnabled(self.__isEnableDrag())

        # 还原 anchor
        self.setTransformationAnchor(self.AnchorUnderMouse)

    def shrinkPicture(self, anchor=QGraphicsView.AnchorUnderMouse):
        """
        缩小图片
        :return:
        """
        if self.zoomInTimes == 0 and not self.__isEnableDrag():
            return

        self.setTransformationAnchor(anchor)

        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)

    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 i
        return self.maxZoomInTimes

    def fitInView(self, item: QGraphicsItem, mode=Qt.AspectRatioMode.KeepAspectRatio):
        """
        缩放场景使其适应窗口大小
        :param item:
        :param mode:
        :return:
        """
        super().fitInView(item, mode)
        self.displayedImageSize = self.__getScaleRatio() * self.pixmap.size()
        self.zoomInTimes = 0

    def resizeEvent(self, event: QResizeEvent):
        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 resetTransform(self):
        """
        重置变换
        :return:
        """
        self.zoomInTimes = 0
        self.__setDragEnabled(False)
        super().resetTransform()

    def wheelEvent(self, e: QWheelEvent):
        """
        滚动鼠标滚轮缩放图片
        :param e:
        :return:
        """
        if e.angleDelta().y() > 0:
            self.enlargePicture()
        else:
            self.shrinkPicture()

该函数可以显示图片实现图片下旋转、放大、缩小等功能

界面实现

pillow源码修改

在实现图片列表时,如果全部加载出来会直接爆内存,因此需要借助pillow生成缩略图,但是pillow最新版本没有适配PyQt5,如果使用需要改源码。

ImageQt.py中修改源码
在这里插入图片描述
将红框中的代码加上就行了。
在这里插入图片描述

ImageQt错误

如果你使用了pillow这个库直接转为QPixmap你就会发现很多问题。

PyQt5+pillow实现缩略图,代码示例:

import sys
from PyQt5.Qt import *
import res.res_rc
from PIL import Image, ImageQt


class Window(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.vBox = QVBoxLayout(self)
        self.vBox.setContentsMargins(0, 0, 0, 0)
        self.vBox.setSpacing(0)
        file = r"G:\手机\壁纸\电脑壁纸\1689637545648.png"
        img = Image.open(file)
        img.thumbnail((200, 120))

        label = QLabel()
        label.setPixmap(ImageQt.toqpixmap(img))

        self.vBox.addWidget(label)


if __name__ == '__main__':
    QApplication.setHighDpiScaleFactorRoundingPolicy(
        Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)

    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(True)
    demo = Window()
    demo.resize(800, 600)
    demo.show()
    sys.exit(app.exec_())

运行结果:

运行后,你会发现基本上所有图片都会变成花屏,于是就直接放弃了,那么只能生成本地图片,然后在显示出来,于是使用本地sqlite数据库,为了使方便引入了SQLAlchemy来管理数据库,别问为什么,问就是不想写SQL,这要也方便了后期拓展

def CreateThumbnail(fileName, saveFile, *, size=(200, 120), **kwargs):
    """创建缩略图"""
    img = Image.open(fileName)
    img.thumbnail(size=size, **kwargs)
    img.save(saveFile)

在这里插入图片描述

主页面布局

在这里插入图片描述

项目开源地址

https://gitee.com/chiyaun/picture-browser.git

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

哪个蓝牙耳机好用?2024最新蓝牙耳机选购指南,实测避坑!

​蓝牙耳机已成为现代生活中不可或缺的一部分。无论你是追求高品质音质、注重佩戴体验&#xff0c;还是在意性价比&#xff0c;市场上总有适合你的那一款。希望通过我的推荐和分析&#xff0c;你能找到一款真正适合自己的蓝牙耳机&#xff0c;让你的音乐之旅更加精彩。 一、选购…

防御保护第八、九、十、十一天笔记

一、内容安全 1、DFI和DPI技术 --- 深度检测技术 DPI是一种基于应用层的流量检测和控制技术&#xff0c;它会对流量进行拆包&#xff0c;分析包头和应用层的内容&#xff0c;从而识别应用程序和应用程序的内容。这种技术增加了对应用层的分析&#xff0c;识别各种应用&#xf…

如何用IP地址找到实际位置?

在互联网世界中&#xff0c;每个设备都有一个独特的标识&#xff0c;那就是IP地址。它不仅是设备在网络中的“身份证”&#xff0c;还承载着设备在网络中的位置信息。那么&#xff0c;我们是否可以通过IP地址来找到设备的实际位置呢&#xff1f;本文将深入探讨这一问题。 一、I…

构建React TodoList应用:管理你的任务清单

构建React TodoList应用&#xff1a;管理你的任务清单 在日常生活和工作中&#xff0c;任务管理是一项至关重要的任务。为了更好地组织和管理我们的工作和生活&#xff0c;我们需要一个高效而简单的任务管理工具。本文将介绍如何使用React框架构建一个功能丰富的TodoList应用&…

华为算法题 go语言或者ptython

1 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返…

抖店还好做吗?新手想抓住2024年抖店的机会,需要做到这几点

我是王路飞。 关于抖店还好做吗&#xff1f;还适合入局吗&#xff1f;类似的问题&#xff0c;这几年每天都有&#xff0c;尤其是在开年后的这段时间。 本身这个时间内就是找项目的黄金时期&#xff0c;再加上抖店的流量和红利都太诱人了。 但很多人不了解电商&#xff0c;不…

洛谷 P1038 [NOIP2003 提高组] 神经网络【拓扑序处理】

原题链接&#xff1a;https://www.luogu.com.cn/problem/P1038 题目背景 人工神经网络&#xff08;Artificial Neural Network&#xff09;是一种新兴的具有自我学习能力的计算系统&#xff0c;在模式识别、函数逼近及贷款风险评估等诸多领域有广泛的应用。对神经网络的研究一…

SAP PP学习笔记03 - SAP中如何设定项目选择

上次这篇文章里面讲了界面的字段显示顺序及是否显示的设置。 并做了 事务代码 控制界面显示的例子。 SAP PP学习笔记02 - PP中配置品目Master时的顺序-CSDN博客 那么&#xff0c;每次控制界面显示什么都要这么挨个 这么设置一遍吗&#xff1f; 那岂不得烦死。 其实SAP里面参…

Python实现歌曲下载程序, 打包exe应用程序

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 环境使用: Python 3.10 Pycharm 模块使用: import requests >>> pip install requests import parsel >>> pip install parsel import pr…

Stable Diffusion 模型分享:Dark Sushi Mix 大颗寿司Mix

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十

Rust: reqwest库示例

一、异步处理单任务 1、cargo.toml [dependencies] tokio { version "1.0.0", features ["full", "tracing"] } tokio-util { version "0.7.0", features ["full"] } tokio-stream { version "0.1" }…

前端工程Bem架构及其封装

文章目录 简介语法在vue3项目中引用sass创建bem.scss文件修改vite.config.tsvue文件中使用结果 这是我学习记录的笔记&#xff0c;如有不正&#xff0c;欢迎补充 简介 首先认识一下什么是bem架构&#xff1f;BEM的意思就是块&#xff08;block&#xff09;、元素&#xff08;e…

操作系统--调度算法

一、进程调度算法&#xff08;CPU调度算法&#xff09; 什么时候会发生 CPU 调度呢&#xff1f;通常有以下四种情况&#xff1a; 「抢占式调度」&#xff1a;进程正在运行的时&#xff0c;可以被打断&#xff0c;使其把 CPU 让给其他进程。那抢占的原则一般有三种&#xff0c…

【网络编程】okhttp深入理解

newCall 实际上是创建了一个 RealCall 有三个参数&#xff1a;OkHttpClient&#xff08;通用配置&#xff0c;超时时间等&#xff09; Request(Http请求所用到的条件&#xff0c;url等) 布尔变量forWebSocket&#xff08;webSocket是一种应用层的交互方式&#xff0c;可双向交互…

符尧大佬一作发文,仅改训练数据,就让LLaMa-2上下文长度扩展20倍!

引言&#xff1a;探索语言模型的长上下文能力 近日&#xff0c;谷歌推出了Gemini Pro 1.5&#xff0c;将上下文窗口长度扩展到100万个tokens&#xff0c;目前领先世界。而其他语言模型也正在不断探索长上下文能力&#xff0c;也就是模型处理和理解超出其训练时所见上下文长度的…

基于飞凌嵌入式RK3568核心板的边缘计算门禁屏解决方案

边缘计算作为一种将计算任务从云端推向网络边缘的新型计算模式&#xff0c;正日益受到各行各业的青睐&#xff0c;并已在我们的生产和生活当中得到了广泛的应用&#xff0c;其中“门禁系统”就是最常见的与边缘计算相结合的应用之一。 传统的门禁系统受限于数据处理能力和网络…

2023年的AI模型学习/部署/优化

可以的话&#xff0c;github上给点一个小心心&#xff0c;感谢观看。 LDC边缘检测的轻量级密集卷积神经网络&#xff1a; meiqisheng/LDC (github.com)https://github.com/meiqisheng/LDC segment-anything分割一切的图像分割算法模型&#xff1a; meiqisheng/segment-anyt…

新手学习Cesium的几点建议

Cesium是当前非常火热的三维数字地球开发框架&#xff0c;很多公司基于Cesium做项目以及形成了自己的产品&#xff0c;关于Cesium的学习&#xff0c;有诸多网站、书籍、学习资料甚至培训教材&#xff0c;这里不再详细推荐&#xff0c;从学习Cesium的角度&#xff0c;资料和教程…

精美的WordPress外贸独立站模板

WordPress外贸独立站主题 简洁实用的WordPress外贸独立站主题&#xff0c;适合时尚服装行业搭建wordpress企业官网使用。 https://www.jianzhanpress.com/?p4999 简洁wordpress独立站模板 绿色精美、简洁大气的wordpress外贸独立网站模板 https://www.jianzhanpress.com/?…

NXP实战笔记(六):S32K3xx基于RTD-SDK在S32DS上配置PWM发波

目录 1、概述 2、SDK配置 2.1、Port配置 2.2、Emios_Mcl_Ip 2.3、Emios_Pwm 2.4、代码示例 1、概述 针对S32K3xx芯片&#xff0c;产生PWM的硬件支持单元仅有两个&#xff0c;分别是eMiosx与Flexio. 生成PWM的顺序&#xff0c;按照单片机所用资源进行初始化执行如下 初始化…