Skip to content

Commit 2c70b8f

Browse files
committed
add p37
1 parent ff4a110 commit 2c70b8f

File tree

2 files changed

+310
-0
lines changed

2 files changed

+310
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# PyQt5基础教程(持续更新中。。。)
22

3+
* p37_大图片放大缩小 [源码下载](p37_大图片放大缩小) [博文地址](https://xugaoxiang.com/2022/12/06/pyqt5-37-image-zoom/)
4+
35
* p36_`QLabel` 滚动条 [源码下载](p36_QLabel滚动条) [博文地址](https://xugaoxiang.com/2022/12/04/pyqt5-36-make-label-scrollable/)
46

57
* p35_`QGraphicsView` 显示图片 [源码下载](p35_QGraphicsView显示图片) [博文地址](https://xugaoxiang.com/2022/12/01/pyqt5-35-qgraphicsview/)

p37_大图片放大缩小/main.py

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
import os.path
2+
import sys
3+
4+
from PyQt5.QtCore import Qt, QRectF, QPoint, QPointF, pyqtSignal, QEvent, QSize
5+
from PyQt5.QtGui import QImage, QPixmap, QPainterPath, QMouseEvent, QPainter, QPen
6+
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QFileDialog, QSizePolicy
7+
from PyQt5.QtWidgets import QApplication
8+
9+
class QtImageViewer(QGraphicsView):
10+
11+
# 定义使用到的鼠标操作信号
12+
leftMouseButtonPressed = pyqtSignal(float, float)
13+
leftMouseButtonReleased = pyqtSignal(float, float)
14+
middleMouseButtonPressed = pyqtSignal(float, float)
15+
middleMouseButtonReleased = pyqtSignal(float, float)
16+
rightMouseButtonPressed = pyqtSignal(float, float)
17+
rightMouseButtonReleased = pyqtSignal(float, float)
18+
leftMouseButtonDoubleClicked = pyqtSignal(float, float)
19+
rightMouseButtonDoubleClicked = pyqtSignal(float, float)
20+
viewChanged = pyqtSignal()
21+
mousePositionOnImageChanged = pyqtSignal(QPoint)
22+
roiSelected = pyqtSignal(int)
23+
24+
def __init__(self):
25+
QGraphicsView.__init__(self)
26+
27+
self.scene = QGraphicsScene()
28+
self.setScene(self.scene)
29+
30+
# 显示的图片
31+
self._image = None
32+
self.aspectRatioMode = Qt.AspectRatioMode.KeepAspectRatio
33+
34+
# 默认没有滚动条
35+
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
36+
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
37+
38+
# 左键区域放大
39+
self.regionZoomButton = Qt.MouseButton.LeftButton
40+
# 右键缩小
41+
self.zoomOutButton = Qt.MouseButton.RightButton
42+
# 中键拖动
43+
self.panButton = Qt.MouseButton.MiddleButton
44+
# 放大或缩小的因子
45+
self.wheelZoomFactor = 1.25
46+
47+
self.zoomStack = []
48+
49+
# Flags for active zooming/panning.
50+
self._isZooming = False
51+
self._isPanning = False
52+
53+
self._pixelPosition = QPoint()
54+
self._scenePosition = QPointF()
55+
56+
self.ROIs = []
57+
58+
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
59+
60+
def hasImage(self):
61+
return self._image is not None
62+
63+
def setImage(self, image):
64+
65+
# 同时支持 QPixmap 和 QImage
66+
if type(image) is QPixmap:
67+
pixmap = image
68+
elif type(image) is QImage:
69+
pixmap = QPixmap.fromImage(image)
70+
else:
71+
raise RuntimeError("ImageViewer.setImage: Argument must be a QImage, QPixmap.")
72+
if self.hasImage():
73+
self._image.setPixmap(pixmap)
74+
else:
75+
self._image = self.scene.addPixmap(pixmap)
76+
77+
self.setSceneRect(QRectF(pixmap.rect())) # Set scene size to image size.
78+
self.updateViewer()
79+
80+
def open(self, filepath=None):
81+
82+
# 不带参数,默认打开文件选择器选择文件
83+
if filepath is None:
84+
filepath, dummy = QFileDialog.getOpenFileName(self, "Open image file.")
85+
if len(filepath) and os.path.isfile(filepath):
86+
image = QImage(filepath)
87+
self.setImage(image)
88+
89+
def updateViewer(self):
90+
91+
if not self.hasImage():
92+
return
93+
if len(self.zoomStack):
94+
self.fitInView(self.zoomStack[-1], self.aspectRatioMode)
95+
else:
96+
self.fitInView(self.sceneRect(), self.aspectRatioMode)
97+
98+
def clearZoom(self):
99+
if len(self.zoomStack) > 0:
100+
self.zoomStack = []
101+
self.updateViewer()
102+
self.viewChanged.emit()
103+
104+
def resizeEvent(self, event):
105+
# 事件响应
106+
self.updateViewer()
107+
108+
def mousePressEvent(self, event):
109+
dummyModifiers = Qt.KeyboardModifier(Qt.KeyboardModifier.ShiftModifier | Qt.KeyboardModifier.ControlModifier
110+
| Qt.KeyboardModifier.AltModifier | Qt.KeyboardModifier.MetaModifier)
111+
if event.modifiers() == dummyModifiers:
112+
QGraphicsView.mousePressEvent(self, event)
113+
event.accept()
114+
return
115+
116+
if (self.regionZoomButton is not None) and (event.button() == self.regionZoomButton):
117+
self._pixelPosition = event.pos()
118+
self.setDragMode(QGraphicsView.DragMode.RubberBandDrag)
119+
QGraphicsView.mousePressEvent(self, event)
120+
event.accept()
121+
self._isZooming = True
122+
return
123+
124+
if (self.zoomOutButton is not None) and (event.button() == self.zoomOutButton):
125+
if len(self.zoomStack):
126+
self.zoomStack.pop()
127+
self.updateViewer()
128+
self.viewChanged.emit()
129+
event.accept()
130+
return
131+
132+
if (self.panButton is not None) and (event.button() == self.panButton):
133+
self._pixelPosition = event.pos() # store pixel position
134+
self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
135+
if self.panButton == Qt.MouseButton.LeftButton:
136+
QGraphicsView.mousePressEvent(self, event)
137+
else:
138+
self.viewport().setCursor(Qt.CursorShape.ClosedHandCursor)
139+
dummyModifiers = Qt.KeyboardModifier(Qt.KeyboardModifier.ShiftModifier
140+
| Qt.KeyboardModifier.ControlModifier
141+
| Qt.KeyboardModifier.AltModifier
142+
| Qt.KeyboardModifier.MetaModifier)
143+
dummyEvent = QMouseEvent(QEvent.Type.MouseButtonPress, QPointF(event.pos()), Qt.MouseButton.LeftButton,
144+
event.buttons(), dummyModifiers)
145+
self.mousePressEvent(dummyEvent)
146+
sceneViewport = self.mapToScene(self.viewport().rect()).boundingRect().intersected(self.sceneRect())
147+
self._scenePosition = sceneViewport.topLeft()
148+
event.accept()
149+
self._isPanning = True
150+
return
151+
152+
scenePos = self.mapToScene(event.pos())
153+
if event.button() == Qt.MouseButton.LeftButton:
154+
self.leftMouseButtonPressed.emit(scenePos.x(), scenePos.y())
155+
elif event.button() == Qt.MouseButton.MiddleButton:
156+
self.middleMouseButtonPressed.emit(scenePos.x(), scenePos.y())
157+
elif event.button() == Qt.MouseButton.RightButton:
158+
self.rightMouseButtonPressed.emit(scenePos.x(), scenePos.y())
159+
160+
QGraphicsView.mousePressEvent(self, event)
161+
162+
def mouseReleaseEvent(self, event):
163+
dummyModifiers = Qt.KeyboardModifier(Qt.KeyboardModifier.ShiftModifier | Qt.KeyboardModifier.ControlModifier
164+
| Qt.KeyboardModifier.AltModifier | Qt.KeyboardModifier.MetaModifier)
165+
if event.modifiers() == dummyModifiers:
166+
QGraphicsView.mouseReleaseEvent(self, event)
167+
event.accept()
168+
return
169+
170+
if (self.regionZoomButton is not None) and (event.button() == self.regionZoomButton):
171+
QGraphicsView.mouseReleaseEvent(self, event)
172+
zoomRect = self.scene.selectionArea().boundingRect().intersected(self.sceneRect())
173+
self.scene.setSelectionArea(QPainterPath())
174+
self.setDragMode(QGraphicsView.DragMode.NoDrag)
175+
zoomPixelWidth = abs(event.pos().x() - self._pixelPosition.x())
176+
zoomPixelHeight = abs(event.pos().y() - self._pixelPosition.y())
177+
if zoomPixelWidth > 3 and zoomPixelHeight > 3:
178+
if zoomRect.isValid() and (zoomRect != self.sceneRect()):
179+
self.zoomStack.append(zoomRect)
180+
self.updateViewer()
181+
self.viewChanged.emit()
182+
event.accept()
183+
self._isZooming = False
184+
return
185+
186+
if (self.panButton is not None) and (event.button() == self.panButton):
187+
if self.panButton == Qt.MouseButton.LeftButton:
188+
QGraphicsView.mouseReleaseEvent(self, event)
189+
else:
190+
self.viewport().setCursor(Qt.CursorShape.ArrowCursor)
191+
dummyModifiers = Qt.KeyboardModifier(Qt.KeyboardModifier.ShiftModifier
192+
| Qt.KeyboardModifier.ControlModifier
193+
| Qt.KeyboardModifier.AltModifier
194+
| Qt.KeyboardModifier.MetaModifier)
195+
dummyEvent = QMouseEvent(QEvent.Type.MouseButtonRelease, QPointF(event.pos()),
196+
Qt.MouseButton.LeftButton, event.buttons(), dummyModifiers)
197+
self.mouseReleaseEvent(dummyEvent)
198+
self.setDragMode(QGraphicsView.DragMode.NoDrag)
199+
if len(self.zoomStack) > 0:
200+
sceneViewport = self.mapToScene(self.viewport().rect()).boundingRect().intersected(self.sceneRect())
201+
delta = sceneViewport.topLeft() - self._scenePosition
202+
self.zoomStack[-1].translate(delta)
203+
self.zoomStack[-1] = self.zoomStack[-1].intersected(self.sceneRect())
204+
self.viewChanged.emit()
205+
event.accept()
206+
self._isPanning = False
207+
return
208+
209+
scenePos = self.mapToScene(event.pos())
210+
if event.button() == Qt.MouseButton.LeftButton:
211+
self.leftMouseButtonReleased.emit(scenePos.x(), scenePos.y())
212+
elif event.button() == Qt.MouseButton.MiddleButton:
213+
self.middleMouseButtonReleased.emit(scenePos.x(), scenePos.y())
214+
elif event.button() == Qt.MouseButton.RightButton:
215+
self.rightMouseButtonReleased.emit(scenePos.x(), scenePos.y())
216+
217+
QGraphicsView.mouseReleaseEvent(self, event)
218+
219+
def mouseDoubleClickEvent(self, event):
220+
if (self.zoomOutButton is not None) and (event.button() == self.zoomOutButton):
221+
self.clearZoom()
222+
event.accept()
223+
return
224+
225+
scenePos = self.mapToScene(event.pos())
226+
if event.button() == Qt.MouseButton.LeftButton:
227+
self.leftMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y())
228+
elif event.button() == Qt.MouseButton.RightButton:
229+
self.rightMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y())
230+
231+
QGraphicsView.mouseDoubleClickEvent(self, event)
232+
233+
def wheelEvent(self, event):
234+
if self.wheelZoomFactor is not None:
235+
if self.wheelZoomFactor == 1:
236+
return
237+
if event.angleDelta().y() < 0:
238+
# zoom in
239+
if len(self.zoomStack) == 0:
240+
self.zoomStack.append(self.sceneRect())
241+
elif len(self.zoomStack) > 1:
242+
del self.zoomStack[:-1]
243+
zoomRect = self.zoomStack[-1]
244+
center = zoomRect.center()
245+
zoomRect.setWidth(zoomRect.width() / self.wheelZoomFactor)
246+
zoomRect.setHeight(zoomRect.height() / self.wheelZoomFactor)
247+
zoomRect.moveCenter(center)
248+
self.zoomStack[-1] = zoomRect.intersected(self.sceneRect())
249+
self.updateViewer()
250+
self.viewChanged.emit()
251+
else:
252+
if len(self.zoomStack) == 0:
253+
return
254+
255+
if len(self.zoomStack) > 1:
256+
del self.zoomStack[:-1]
257+
zoomRect = self.zoomStack[-1]
258+
center = zoomRect.center()
259+
zoomRect.setWidth(zoomRect.width() * self.wheelZoomFactor)
260+
zoomRect.setHeight(zoomRect.height() * self.wheelZoomFactor)
261+
zoomRect.moveCenter(center)
262+
self.zoomStack[-1] = zoomRect.intersected(self.sceneRect())
263+
if self.zoomStack[-1] == self.sceneRect():
264+
self.zoomStack = []
265+
self.updateViewer()
266+
self.viewChanged.emit()
267+
event.accept()
268+
return
269+
270+
QGraphicsView.wheelEvent(self, event)
271+
272+
def mouseMoveEvent(self, event):
273+
if self._isPanning:
274+
QGraphicsView.mouseMoveEvent(self, event)
275+
if len(self.zoomStack) > 0:
276+
sceneViewport = self.mapToScene(self.viewport().rect()).boundingRect().intersected(self.sceneRect())
277+
delta = sceneViewport.topLeft() - self._scenePosition
278+
self._scenePosition = sceneViewport.topLeft()
279+
self.zoomStack[-1].translate(delta)
280+
self.zoomStack[-1] = self.zoomStack[-1].intersected(self.sceneRect())
281+
self.updateViewer()
282+
self.viewChanged.emit()
283+
284+
scenePos = self.mapToScene(event.pos())
285+
if self.sceneRect().contains(scenePos):
286+
x = int(round(scenePos.x() - 0.5))
287+
y = int(round(scenePos.y() - 0.5))
288+
imagePos = QPoint(x, y)
289+
else:
290+
imagePos = QPoint(-1, -1)
291+
self.mousePositionOnImageChanged.emit(imagePos)
292+
293+
QGraphicsView.mouseMoveEvent(self, event)
294+
295+
def enterEvent(self, event):
296+
self.setCursor(Qt.CursorShape.CrossCursor)
297+
298+
def leaveEvent(self, event):
299+
self.setCursor(Qt.CursorShape.ArrowCursor)
300+
301+
if __name__ == '__main__':
302+
303+
app = QApplication(sys.argv)
304+
viewer = QtImageViewer()
305+
# viewer.open(filepath='test.jpg')
306+
viewer.open()
307+
viewer.show()
308+
sys.exit(app.exec())

0 commit comments

Comments
 (0)