一個自制的圖片瀏覽器,如果不想安裝
qfluentwidgets
,CommandBarView
可以使用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
QImage
與QPixmap
不太支持大文件瀏覽,或者未知圖片類型,使用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())