-
-
Notifications
You must be signed in to change notification settings - Fork 2k
/
ImageView.py
240 lines (208 loc) · 8.23 KB
/
ImageView.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on 2021/4/15
@author: Irony
@site: https://pyqt.site , https://github.com/PyQt5
@email: [email protected]
@file: ImageView
@description:
"""
import os
try:
from PyQt5.QtCore import QPointF, Qt, QRectF, QSizeF
from PyQt5.QtGui import QStandardItem, QStandardItemModel, QPainter, QColor, QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QListView, QGraphicsView, QGraphicsPixmapItem, QGraphicsScene
except ImportError:
from PySide2.QtCore import QPointF, Qt, QRectF, QSizeF
from PySide2.QtGui import QStandardItem, QStandardItemModel, QPainter, QColor, QImage, QPixmap
from PySide2.QtWidgets import QApplication, 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')
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_())