diff --git a/QListView/ImageView.py b/QListView/ImageView.py new file mode 100644 index 0000000..2f3aa01 --- /dev/null +++ b/QListView/ImageView.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2021/4/15 +@author: Irony +@site: https://github.com/PyQt5 +@email: 892768447@qq.com +@file: ImageView +@description: +""" +import os + +from PyQt5.QtCore import QPointF, Qt, QRectF, QSizeF +from PyQt5.QtGui import QStandardItem, QStandardItemModel, QPainter, QColor, QImage, QPixmap +from PyQt5.QtWidgets import QListView, QGraphicsView, QGraphicsPixmapItem, QGraphicsScene + +ScrollPixel = 40 + + +class BigImageView(QGraphicsView): + """图片查看控件""" + + def __init__(self, *args, **kwargs): + image = kwargs.pop('image', None) + background = kwargs.pop('background', None) + super(BigImageView, self).__init__(*args, **kwargs) + self.setCursor(Qt.OpenHandCursor) + self.setBackground(background) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing | + QPainter.SmoothPixmapTransform) + self.setCacheMode(self.CacheBackground) + self.setViewportUpdateMode(self.SmartViewportUpdate) + self._item = QGraphicsPixmapItem() # 放置图像 + self._item.setFlags(QGraphicsPixmapItem.ItemIsFocusable | + QGraphicsPixmapItem.ItemIsMovable) + self._scene = QGraphicsScene(self) # 场景 + self.setScene(self._scene) + self._scene.addItem(self._item) + rect = QApplication.instance().desktop().availableGeometry() + self.resize(int(rect.width() * 2 / 3), int(rect.height() * 2 / 3)) + + self.pixmap = None + self._delta = 0.1 # 缩放 + self.setPixmap(image) + + def setBackground(self, color): + """设置背景颜色 + :param color: 背景颜色 + :type color: QColor or str or GlobalColor + """ + if isinstance(color, QColor): + self.setBackgroundBrush(color) + elif isinstance(color, (str, Qt.GlobalColor)): + color = QColor(color) + if color.isValid(): + self.setBackgroundBrush(color) + + def setPixmap(self, pixmap, fitIn=True): + """加载图片 + :param pixmap: 图片或者图片路径 + :param fitIn: 是否适应 + :type pixmap: QPixmap or QImage or str + :type fitIn: bool + """ + if isinstance(pixmap, QPixmap): + self.pixmap = pixmap + elif isinstance(pixmap, QImage): + self.pixmap = QPixmap.fromImage(pixmap) + elif isinstance(pixmap, str) and os.path.isfile(pixmap): + self.pixmap = QPixmap(pixmap) + else: + return + self._item.setPixmap(self.pixmap) + self._item.update() + self.setSceneDims() + if fitIn: + self.fitInView(QRectF(self._item.pos(), QSizeF( + self.pixmap.size())), Qt.KeepAspectRatio) + self.update() + + def setSceneDims(self): + if not self.pixmap: + return + self.setSceneRect(QRectF(QPointF(0, 0), QPointF(self.pixmap.width(), self.pixmap.height()))) + + def fitInView(self, rect, flags=Qt.IgnoreAspectRatio): + """剧中适应 + :param rect: 矩形范围 + :param flags: + :return: + """ + if not self.scene() or rect.isNull(): + return + unity = self.transform().mapRect(QRectF(0, 0, 1, 1)) + self.scale(1 / unity.width(), 1 / unity.height()) + viewRect = self.viewport().rect() + sceneRect = self.transform().mapRect(rect) + x_ratio = viewRect.width() / sceneRect.width() + y_ratio = viewRect.height() / sceneRect.height() + if flags == Qt.KeepAspectRatio: + x_ratio = y_ratio = min(x_ratio, y_ratio) + elif flags == Qt.KeepAspectRatioByExpanding: + x_ratio = y_ratio = max(x_ratio, y_ratio) + self.scale(x_ratio, y_ratio) + self.centerOn(rect.center()) + + def wheelEvent(self, event): + if event.angleDelta().y() > 0: + self.zoomIn() + else: + self.zoomOut() + + def zoomIn(self): + """放大""" + self.zoom(1 + self._delta) + + def zoomOut(self): + """缩小""" + self.zoom(1 - self._delta) + + def zoom(self, factor): + """缩放 + :param factor: 缩放的比例因子 + """ + _factor = self.transform().scale( + factor, factor).mapRect(QRectF(0, 0, 1, 1)).width() + if _factor < 0.07 or _factor > 100: + # 防止过大过小 + return + self.scale(factor, factor) + + +class ImageView(QListView): + + def __init__(self, *args, **kwargs): + super(ImageView, self).__init__(*args, **kwargs) + self.setFrameShape(self.NoFrame) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setEditTriggers(self.NoEditTriggers) + self.setDropIndicatorShown(True) + self.setDragDropMode(self.DragDrop) + self.setDefaultDropAction(Qt.IgnoreAction) + self.setSelectionMode(self.ExtendedSelection) + self.setVerticalScrollMode(self.ScrollPerPixel) + self.setHorizontalScrollMode(self.ScrollPerPixel) + self.setFlow(self.LeftToRight) + self.setWrapping(True) + self.setResizeMode(self.Adjust) + self.setSpacing(6) + self.setViewMode(self.IconMode) + self.setWordWrap(True) + self.setSelectionRectVisible(True) + self.setContextMenuPolicy(Qt.CustomContextMenu) + # 解决拖动到顶部或者底部自动滚动 + self.setAutoScrollMargin(150) + self.verticalScrollBar().setSingleStep(ScrollPixel) + # 设置model + self.dmodel = QStandardItemModel(self) + self.setModel(self.dmodel) + + # 大图控件 + self.bigView = BigImageView(background='#323232') + + def addItem(self, image): + if isinstance(image, str): + image = QPixmap(image) + # 添加一个item + item = QStandardItem() + # 记录原始图片 + item.setData(image, Qt.UserRole + 1) # 用于双击的时候取出来 + # 缩放成小图并显示 + item.setData(image.scaled(60, 60, Qt.IgnoreAspectRatio, Qt.SmoothTransformation), Qt.DecorationRole) + # 添加item到界面中 + self.dmodel.appendRow(item) + + def count(self): + return self.dmodel.rowCount() + + def setCurrentRow(self, row): + self.setCurrentIndex(self.dmodel.index(row, 0)) + + def currentRow(self): + return self.currentIndex().row() + + def updateGeometries(self): + # 一次滑动20px + super(ImageView, self).updateGeometries() + self.verticalScrollBar().setSingleStep(ScrollPixel) + + def closeEvent(self, event): + # 关闭预览窗口 + self.bigView.close() + super(ImageView, self).closeEvent(event) + + def wheelEvent(self, event): + # 修复滑动bug + if self.flow() == QListView.LeftToRight: + bar = self.horizontalScrollBar() + value = ScrollPixel if event.angleDelta().y() < 0 else (0 - ScrollPixel) + bar.setSliderPosition(bar.value() + value) + else: + super(ImageView, self).wheelEvent(event) + + def mouseDoubleClickEvent(self, event): + # 列表双击,如果有item则进入item处理流程,否则调用打开图片功能 + index = self.indexAt(event.pos()) + if index and index.isValid(): + item = self.dmodel.itemFromIndex(index) + if item: + # 取出原图用来新窗口显示 + image = item.data(Qt.UserRole + 1) + self.bigView.setPixmap(image) + self.bigView.show() + return + super(ImageView, self).mouseDoubleClickEvent(event) + + +if __name__ == '__main__': + import sys + import cgitb + + cgitb.enable(format='text') + from PyQt5.QtWidgets import QApplication + + app = QApplication(sys.argv) + w = ImageView() + w.show() + + # 添加模拟图片 + for i in range(3): + for name in os.listdir('ScreenShot'): + w.addItem(os.path.join('ScreenShot', name)) + sys.exit(app.exec_())