当前位置: 首页 > news >正文

Qt小组件 - 3 imageLabel

ImageLabel之前有过一个文章,但是用的不方便,在这重新写一下,弥补一下qfluentwidgets库的功能欠缺

特点

  • 支持http图片
  • 支持bytes、base64的转码
  • 支持图片文件蒙版填充
  • 支持图片圆角
  • 重写setScaledContents设置图片是否居中显示

ZoomImageLabel 缩放动画标签,仿包子漫画的一个展示标签,包子漫画

# coding: utf-8
import base64
from pathlib import Path
from random import randint
from typing import Unionfrom PySide6.QtCore import Signal, QEvent, QSize, Qt, Property, QByteArray, QBuffer, QIODevice, QPropertyAnimation, \QRectF, QSizeF, QPointF, QUrl
from PySide6.QtGui import QImage, QPixmap, QImageReader, QMovie, QMouseEvent, QPaintEvent, QPainter, QPainterPath, \QColor
from PySide6.QtWidgets import QLabel
from PySide6.QtNetwork import QNetworkReply, QNetworkRequest, QNetworkAccessManager
from qfluentwidgets.common.overload import singledispatchmethodfrom common import USER_AGENTclass ImageLabel(QLabel):clicked = Signal()finished = Signal()@singledispatchmethoddef __init__(self, parent=None):super().__init__(parent)self.image = QImage()self.__maskColor = QColor(randint(0, 255), randint(0, 255), randint(0, 255), 50)self.__maskEnabled = Falseself.__scaledContents = Falseself.__topLeftRadius = 0self.__topRightRadius = 0self.__bottomLeftRadius = 0self.__bottomRightRadius = 0self.__radius = 0self.manager = QNetworkAccessManager(self)self._postInit()@__init__.registerdef _(self, image: Union[str, QImage, QPixmap, Path], parent=None):self.__init__(parent)self.setImage(image)def _postInit(self):passdef _onFrameChanged(self, index: int):self.image = self.movie().currentImage()self.update()def setBorderRadius(self, topLeft: int, topRight: int, bottomLeft: int, bottomRight: int):""" 设置图像的边界半径 """self.__topLeftRadius = topLeftself.__topRightRadius = topRightself.__bottomLeftRadius = bottomLeftself.__bottomRightRadius = bottomRightself.update()def _onFinished(self, reply: QNetworkReply):image = QImage()if reply.error() == QNetworkReply.NetworkError.NoError:data = reply.readAll()image.loadFromData(data)else:print(f"Network error: {reply.errorString()}")self.setImage(image)reply.deleteLater()def setUrl(self, url: Union[str, QUrl]):request = QNetworkRequest(QUrl(url))request.setHeader(QNetworkRequest.KnownHeaders.UserAgentHeader, USER_AGENT.encode())reply = self.manager.get(request)reply.finished.connect(lambda: self._onFinished(reply))def setImage(self, image: Union[str, QImage, QPixmap, Path]):if isinstance(image, (str, Path)):reader = QImageReader(str(image))if reader.supportsAnimation():self.setMovie(QMovie(str(image)))else:image = reader.read()elif isinstance(image, QPixmap):image = image.toImage()self.image = imageself.update()self.finished.emit()def setPixmap(self, pixmap: Union[str, QImage, QPixmap, Path]):self.setImage(pixmap)def pixmap(self) -> QPixmap:return QPixmap.fromImage(self.image)def setMovie(self, movie: QMovie):super().setMovie(movie)self.movie().start()self.image = self.movie().currentImage()self.movie().frameChanged.connect(self._onFrameChanged)def scaledToWidth(self, width: int):if self.isNull():returnh = int(width / self.image.width() * self.image.height())self.setFixedSize(width, h)if self.movie():self.movie().setScaledSize(QSize(width, h))def scaledToHeight(self, height: int):if self.isNull():returnw = int(height / self.image.height() * self.image.width())self.setFixedSize(w, height)if self.movie():self.movie().setScaledSize(QSize(w, height))def setScaledSize(self, size: QSize):if self.isNull():returnself.setFixedSize(size)if self.movie():self.movie().setScaledSize(size)def sizeHint(self) -> QSize:if self.image.isNull():return super().sizeHint()else:return self.image.size()def isNull(self) -> bool:return self.image.isNull()def mouseReleaseEvent(self, event: QMouseEvent):pos = event.position().toPoint()if event.button() == Qt.MouseButton.LeftButton and self.rect().contains(pos):self.clicked.emit()super().mouseReleaseEvent(event)def setRadius(self, radius: int):self.__radius = radiusself.__topLeftRadius = self.__topRightRadius = self.__bottomLeftRadius = self.__bottomRightRadius = radiusself.update()def getRadius(self) -> int:return self.__radiusdef setTopLeftRadius(self, radius: int):self.__topLeftRadius = radiusself.update()def getTopLeftRadius(self) -> int:return self.__topLeftRadiusdef setTopRightRadius(self, radius: int):self.__topRightRadius = radiusself.update()def getTopRightRadius(self) -> int:return self.__topRightRadiusdef setBottomLeftRadius(self, radius: int):self.__bottomLeftRadius = radiusself.update()def getBottomLeftRadius(self) -> int:return self.__bottomLeftRadiusdef setBottomRightRadius(self, radius: int):self.__bottomRightRadius = radiusself.update()def getBottomRightRadius(self) -> int:return self.__bottomRightRadiusdef setScaledContents(self, contents: bool):self.__scaledContents = contentsself.update()def getScaledContents(self) -> bool:return self.__scaledContentsdef setMaskColor(self, color: QColor):self.__maskColor = colorself.update()def getMaskColor(self) -> QColor:return self.__maskColordef setEnabledMask(self, enabled: bool):self.__maskEnabled = enabledself.update()def getEnabledMask(self) -> bool:return self.__maskEnableddef _creatPainterPath(self):path = QPainterPath()w, h = self.width(), self.height()# top linepath.moveTo(self.topLeftRadius, 0)path.lineTo(w - self.topRightRadius, 0)# top right arcd = self.topRightRadius * 2path.arcTo(w - d, 0, d, d, 90, -90)# right linepath.lineTo(w, h - self.bottomRightRadius)# bottom right arcd = self.bottomRightRadius * 2path.arcTo(w - d, h - d, d, d, 0, -90)# bottom linepath.lineTo(self.bottomLeftRadius, h)# bottom left arcd = self.bottomLeftRadius * 2path.arcTo(0, h - d, d, d, -90, -90)# left linepath.lineTo(0, self.topLeftRadius)# top left arcd = self.topLeftRadius * 2path.arcTo(0, 0, d, d, -180, -90)return pathdef _creatPainterImage(self):if self.scaledContents:image = self.image.scaled(self.size() * self.devicePixelRatioF(),Qt.AspectRatioMode.KeepAspectRatioByExpanding,Qt.TransformationMode.SmoothTransformation)  # type: QImageiw, ih = image.width(), image.height()w = self.width() * self.devicePixelRatioF()h = self.height() * self.devicePixelRatioF()x, y = (iw - w) / 2, (ih - h) / 2image = image.copy(int(x), int(y), int(w), int(h))return imageelse:image = self.image.scaled(self.size() * self.devicePixelRatioF(),Qt.AspectRatioMode.IgnoreAspectRatio,Qt.TransformationMode.SmoothTransformation)return imagedef _creatPainterRect(self):return self.rect()def paintEvent(self, event: QPaintEvent):if self.isNull():returnpainter = QPainter(self)painter.setRenderHint(QPainter.RenderHint.Antialiasing)painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)painter.setRenderHint(QPainter.RenderHint.LosslessImageRendering)painter.setPen(Qt.PenStyle.NoPen)painter.setClipPath(self._creatPainterPath())rect = self._creatPainterRect()painter.drawImage(rect, self._creatPainterImage())if self.enabledMask:painter.fillRect(rect, self.maskColor)painter.end()def save(self, fileName: Union[str, QIODevice], *args, **kwargs):if self.isNull():returnself.image.save(fileName, *args, **kwargs)def bytes(self, format: str = 'PNG') -> bytes:"""将图片转换为字节数组:param format: 图片格式:return:"""if self.isNull():return b''byte_array = QByteArray()buffer = QBuffer(byte_array)buffer.open(QIODevice.OpenModeFlag.WriteOnly)self.save(buffer, format.upper())return byte_array.data()def base64(self, format: str = 'PNG', prefix: str = 'data:image/{};base64,') -> str:""":param format: 图片格式:param prefix: 前缀:return: base64字符串"""if self.image.isNull():return ''suffix = format.upper()if suffix == 'JPG':suffix = 'JPEG'prefix = prefix.format(suffix)base64_str = prefix + base64.b64encode(self.bytes(suffix)).decode()return base64_strscaledContents = Property(bool, getScaledContents, setScaledContents)topLeftRadius = Property(int, getTopLeftRadius, setTopLeftRadius)topRightRadius = Property(int, getTopRightRadius, setTopRightRadius)bottomLeftRadius = Property(int, getBottomLeftRadius, setBottomLeftRadius)bottomRightRadius = Property(int, getBottomRightRadius, setBottomRightRadius)maskColor = Property(QColor, getMaskColor, setMaskColor)enabledMask = Property(bool, getEnabledMask, setEnabledMask)radius = Property(int, getRadius, setRadius)class ZoomImageLabel(ImageLabel):""" 缩放图像标签 """def _postInit(self):self.__zoomRatio = 1.0  # 缩放比例self.animation = QPropertyAnimation(self, b"zoomRatio", self)self.animation.setDuration(300)self.animation.setStartValue(1.0)self.animation.setEndValue(1.4)self.setRadius(5)self.setScaledContents(True)def setMinZoomRatio(self, minZoomRatio: float):self.animation.setStartValue(minZoomRatio)def setMaxZoomRatio(self, maxZoomRatio: float):self.animation.setEndValue(maxZoomRatio)def setZoomRatio(self, zoomRatio: float):self.__zoomRatio = zoomRatioself.update()def getZoomRatio(self) -> float:return self.__zoomRatiodef event(self, event: QEvent):if event.type() == QEvent.Type.Enter:self.animation.setDirection(QPropertyAnimation.Direction.Forward)self.animation.start()elif event.type() == QEvent.Type.Leave:self.animation.setDirection(QPropertyAnimation.Direction.Backward)self.animation.start()return super().event(event)def _creatPainterRect(self):image = self._creatPainterImage()imageRatio = image.width() / image.height()rectF = QRectF()rectF.setSize(QSizeF(imageRatio * self.height() * self.zoomRatio, self.height() * self.zoomRatio))rectF.moveCenter(QPointF(self.rect().center().x(), self.rect().center().y()))return rectFzoomRatio = Property(float, getZoomRatio, setZoomRatio)

singledispatchmethod

# coding: utf-8
from functools import singledispatch, update_wrapperclass singledispatchmethod:"""Single-dispatch generic method descriptor.Supports wrapping existing descriptors and handles non-descriptorcallables as instance methods."""def __init__(self, func):if not callable(func) and not hasattr(func, "__get__"):raise TypeError(f"{func!r} is not callable or a descriptor")self.dispatcher = singledispatch(func)self.func = funcdef register(self, cls, method=None):"""generic_method.register(cls, func) -> funcRegisters a new implementation for the given *cls* on a *generic_method*."""return self.dispatcher.register(cls, func=method)def __get__(self, obj, cls=None):def _method(*args, **kwargs):if args:method = self.dispatcher.dispatch(args[0].__class__)else:method = self.funcfor v in kwargs.values():if v.__class__ in self.dispatcher.registry:method = self.dispatcher.dispatch(v.__class__)if method is not self.func:breakreturn method.__get__(obj, cls)(*args, **kwargs)_method.__isabstractmethod__ = self.__isabstractmethod___method.register = self.registerupdate_wrapper(_method, self.func)return _method@propertydef __isabstractmethod__(self):return getattr(self.func, '__isabstractmethod__', False)
http://www.dtcms.com/a/278974.html

相关文章:

  • 【CV综合实战】基于深度学习的工业压力表智能检测与读数系统【3】使用OpenCV读取分割后的压力表读数
  • 《C++内存泄漏8大战场:Qt/MFC实战详解 + 面试高频陷阱破解》
  • 机器学习中的朴素贝叶斯(Naive Bayes)模型
  • AI日报 - 2025年07月14日
  • 认识下计算机视觉中的人脸识别
  • 网络准入控制系统的作用解析,2025年保障企业入网安全第一道防线
  • 【邀请函】网易灵动露天矿山具身智能技术发布会,7月26日上海见
  • 【笔记】chrome 无法打开特定协议或访问特定协议时卡死
  • AI香烟检测实战:YOLO11模型训练全过程解析
  • 多尺度频率辅助类 Mamba 线性注意力模块(MFM),融合频域和空域特征,提升多尺度、复杂场景下的目标检测能力
  • Docker 拉取镜像并离线迁移至云桌面指南(以Redis为例)
  • 【API测试】Apifox、Apipost、Postman测试工具详解,Mock介绍
  • docker私有仓库
  • Java 树形结构、层级结构数据构建
  • 密码学中立方攻击的另类应用
  • 力扣454.四数相加Ⅱ
  • idea删除的文件怎么找回
  • 【第一章编辑器开发基础第二节编辑器布局_1水平与垂直布局(1/4)】
  • git项目,有idea文件夹,怎么去掉
  • 【第一章编辑器开发基础第一节绘制编辑器元素_6滑动条控件(6/7)】
  • 衡石科技技术手册--仪表盘过滤控件详解
  • SpringBoot集成SAP,本地IDEA启动和Windows服务器部署
  • 第八章排序 选择题
  • 【HarmonyOS】元服务入门详解 (一)
  • 从“直觉抢答”到“深度思考”:大模型的“慢思考”革命,思维链、树、图如何让AI越来越像人?
  • 生产者消费者问题,详解(操作系统os)
  • 扩散生成基础原理(二)——DDPM概率去噪扩散模型
  • 1.2.1 面向对象详解——AI教你学Django
  • git 下载报错:fetch-pack: unexpected disconnect while reading sideband packet
  • 139-CNN-BiLSTM-Selfattention-ABKDE预测模型!