链接两个 qgraphicsview 对象之间的缩放
Linking zoom between two qgraphicsview objects
我正在尝试 link 两个 qgraphicsview 对象,这样当一个 window 放大时,另一个 qgraphicsview 也会放大。我试图通过在 window 将要缩放时调用测试函数来完成此操作。然后我尝试在另一个 qgraphicsview 对象中调用 self.scale(factor,factor) 。我得到一个错误:AttributeError: 'PhotoViewer' object has no attribute 'viewer'。我如何获得 我正在尝试调用 class ui self.viewer 但从错误判断我仍在 Photoviewer 对象中。当一个 window 放大时,如何调用另一个 windows self.scale?提前致谢。
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QPixmap, QPainter, QPen
import sys
class PhotoViewer(QtWidgets.QGraphicsView):
photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self, parent,num_view):
super(PhotoViewer, self).__init__(parent)
#to track which viewer
self.num_view=num_view
self.drawmode=0
self._zoom = 0
self.drawing = False
self.lastPoint = QPoint()
self.image=False
self.image=QPixmap(r"image.jpg")
self._empty = True
self._scene = QtWidgets.QGraphicsScene(self)
self._photo = QtWidgets.QGraphicsPixmapItem()
self._scene.addItem(self._photo)
self.setScene(self._scene)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
self.setFrameShape(QtWidgets.QFrame.NoFrame)
def hasPhoto(self):
return not self._empty
def fitInView(self, scale=True):
rect = QtCore.QRectF(self._photo.pixmap().rect())
if not rect.isNull():
self.setSceneRect(rect)
if self.hasPhoto():
unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPhoto(self, pixmap=None):
self._zoom = 0
if pixmap and not pixmap.isNull():
self._empty = False
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
self._photo.setPixmap(pixmap)
else:
self._empty = True
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
self._photo.setPixmap(QtGui.QPixmap())
self.fitInView()
def wheelEvent(self, event):
if self.hasPhoto():
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
ui.test(self,self.num_view,factor)
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def toggleDragMode(self):
if self.dragMode() == QtWidgets.QGraphicsView.ScrollHandDrag:
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
elif not self._photo.pixmap().isNull():
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
class ui(QtWidgets.QWidget):
def __init__(self):
super(ui, self).__init__()
self.viewer = PhotoViewer(self,1)
self.viewer2 = PhotoViewer(self,2)
# 'Load image' button
self.btnLoad = QtWidgets.QToolButton(self)
self.btnLoad.setText('Load image')
self.btnLoad.clicked.connect(self.loadImage)
# draw mode
self.btndraw = QtWidgets.QToolButton(self)
self.btndraw.setText('Draw Mode')
self.btndraw.clicked.connect(self.drawmode)
# Button to change from drag/pan to getting pixel info
self.btnPixInfo = QtWidgets.QToolButton(self)
self.btnPixInfo.setText('Enter pixel info mode')
self.btnPixInfo.clicked.connect(self.pixInfo)
self.editPixInfo = QtWidgets.QLineEdit(self)
self.editPixInfo.setReadOnly(True)
self.viewer.photoClicked.connect(self.photoClicked)
# Arrange layout
VBlayout = QtWidgets.QVBoxLayout(self)
HBlayout2 = QtWidgets.QHBoxLayout()
HBlayout2.setAlignment(QtCore.Qt.AlignLeft)
HBlayout2.addWidget(self.viewer2)
HBlayout2.addWidget(self.viewer)
HBlayout = QtWidgets.QHBoxLayout()
HBlayout.setAlignment(QtCore.Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
HBlayout.addWidget(self.btnPixInfo)
HBlayout.addWidget(self.btndraw)
HBlayout.addWidget(self.editPixInfo)
VBlayout.addLayout(HBlayout2)
VBlayout.addLayout(HBlayout)
#scroll bars
self.viewer.horizontalScrollBar().valueChanged.connect(self.horizontal_scroll)
self.viewer.verticalScrollBar().valueChanged.connect(self.vertical_scroll)
self.viewer2.horizontalScrollBar().valueChanged.connect(self.horizontal_scroll2)
self.viewer2.verticalScrollBar().valueChanged.connect(self.vertical_scroll2)
def test(self,num,factor):
if num==1:
self.viewer2.scale(factor,factor)
if num==2:
self.viewer.scale(factor,factor)
#auto scroll
def horizontal_scroll(self):
self.viewer2.horizontalScrollBar().setValue(self.viewer.horizontalScrollBar().value())
def horizontal_scroll2(self):
self.viewer.horizontalScrollBar().setValue(self.viewer2.horizontalScrollBar().value())
def vertical_scroll(self):
self.viewer2.verticalScrollBar().setValue(self.viewer.verticalScrollBar().value())
def vertical_scroll2(self):
self.viewer.verticalScrollBar().setValue(self.viewer2.verticalScrollBar().value())
def loadImage(self):
self.viewer.setPhoto(QtGui.QPixmap(r'temp.png'))
self.viewer2.setPhoto(QtGui.QPixmap(r'temp.png'))
self.image=QPixmap(r"image.jpg")
def drawmode(self):
self.viewer.toggleDrawMode()
def pixInfo(self):
self.viewer.toggleDragMode()
def photoClicked(self, pos):
if self.viewer.dragMode() == QtWidgets.QGraphicsView.NoDrag:
self.editPixInfo.setText('%d, %d' % (pos.x(), pos.y()))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = ui()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
与其尝试直接缩放视图,不如为此使用信号:
将以下代码添加到 PhotoViewer
:
class PhotoViewer(QtWidgets.QGraphicsView):
scaled = QtCore.pyqtSignal(int, int, QtGui.QTransform, int)
....
def scale(self, horz, vert):
super().scale(horz, vert)
self.scaled.emit(self.horizontalScrollBar().value(),
self.verticalScrollBar().value(),
self.transform(),
self._zoom
)
def set_transform(self, horz_scroll, vert_scroll, transform, zoom):
# temporary block signals from scroll bars to prevent interference
horz_blocked = self.horizontalScrollBar().blockSignals(True)
vert_blocked = self.verticalScrollBar().blockSignals(True)
self._zoom = zoom
self.setTransform(transform)
dx = horz_scroll - self.horizontalScrollBar().value()
dy = vert_scroll - self.verticalScrollBar().value()
self.horizontalScrollBar().setValue(dx)
self.verticalScrollBar().setValue(dy)
self.horizontalScrollBar().blockSignals(horz_blocked)
self.verticalScrollBar().blockSignals(vert_blocked)
然后 ui
:
class ui(QtWidgets.QWidget):
def __init__(self):
....
self.viewer.scaled.connect(self.viewer2.set_transform)
self.viewer2.scaled.connect(self.viewer.set_transform)
同时删除对 ui.test()
表单 PhotoViewer.wheelEvent
的调用。
我正在尝试 link 两个 qgraphicsview 对象,这样当一个 window 放大时,另一个 qgraphicsview 也会放大。我试图通过在 window 将要缩放时调用测试函数来完成此操作。然后我尝试在另一个 qgraphicsview 对象中调用 self.scale(factor,factor) 。我得到一个错误:AttributeError: 'PhotoViewer' object has no attribute 'viewer'。我如何获得 我正在尝试调用 class ui self.viewer 但从错误判断我仍在 Photoviewer 对象中。当一个 window 放大时,如何调用另一个 windows self.scale?提前致谢。
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QPixmap, QPainter, QPen
import sys
class PhotoViewer(QtWidgets.QGraphicsView):
photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self, parent,num_view):
super(PhotoViewer, self).__init__(parent)
#to track which viewer
self.num_view=num_view
self.drawmode=0
self._zoom = 0
self.drawing = False
self.lastPoint = QPoint()
self.image=False
self.image=QPixmap(r"image.jpg")
self._empty = True
self._scene = QtWidgets.QGraphicsScene(self)
self._photo = QtWidgets.QGraphicsPixmapItem()
self._scene.addItem(self._photo)
self.setScene(self._scene)
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
self.setFrameShape(QtWidgets.QFrame.NoFrame)
def hasPhoto(self):
return not self._empty
def fitInView(self, scale=True):
rect = QtCore.QRectF(self._photo.pixmap().rect())
if not rect.isNull():
self.setSceneRect(rect)
if self.hasPhoto():
unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPhoto(self, pixmap=None):
self._zoom = 0
if pixmap and not pixmap.isNull():
self._empty = False
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
self._photo.setPixmap(pixmap)
else:
self._empty = True
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
self._photo.setPixmap(QtGui.QPixmap())
self.fitInView()
def wheelEvent(self, event):
if self.hasPhoto():
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
ui.test(self,self.num_view,factor)
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def toggleDragMode(self):
if self.dragMode() == QtWidgets.QGraphicsView.ScrollHandDrag:
self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
elif not self._photo.pixmap().isNull():
self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
class ui(QtWidgets.QWidget):
def __init__(self):
super(ui, self).__init__()
self.viewer = PhotoViewer(self,1)
self.viewer2 = PhotoViewer(self,2)
# 'Load image' button
self.btnLoad = QtWidgets.QToolButton(self)
self.btnLoad.setText('Load image')
self.btnLoad.clicked.connect(self.loadImage)
# draw mode
self.btndraw = QtWidgets.QToolButton(self)
self.btndraw.setText('Draw Mode')
self.btndraw.clicked.connect(self.drawmode)
# Button to change from drag/pan to getting pixel info
self.btnPixInfo = QtWidgets.QToolButton(self)
self.btnPixInfo.setText('Enter pixel info mode')
self.btnPixInfo.clicked.connect(self.pixInfo)
self.editPixInfo = QtWidgets.QLineEdit(self)
self.editPixInfo.setReadOnly(True)
self.viewer.photoClicked.connect(self.photoClicked)
# Arrange layout
VBlayout = QtWidgets.QVBoxLayout(self)
HBlayout2 = QtWidgets.QHBoxLayout()
HBlayout2.setAlignment(QtCore.Qt.AlignLeft)
HBlayout2.addWidget(self.viewer2)
HBlayout2.addWidget(self.viewer)
HBlayout = QtWidgets.QHBoxLayout()
HBlayout.setAlignment(QtCore.Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
HBlayout.addWidget(self.btnPixInfo)
HBlayout.addWidget(self.btndraw)
HBlayout.addWidget(self.editPixInfo)
VBlayout.addLayout(HBlayout2)
VBlayout.addLayout(HBlayout)
#scroll bars
self.viewer.horizontalScrollBar().valueChanged.connect(self.horizontal_scroll)
self.viewer.verticalScrollBar().valueChanged.connect(self.vertical_scroll)
self.viewer2.horizontalScrollBar().valueChanged.connect(self.horizontal_scroll2)
self.viewer2.verticalScrollBar().valueChanged.connect(self.vertical_scroll2)
def test(self,num,factor):
if num==1:
self.viewer2.scale(factor,factor)
if num==2:
self.viewer.scale(factor,factor)
#auto scroll
def horizontal_scroll(self):
self.viewer2.horizontalScrollBar().setValue(self.viewer.horizontalScrollBar().value())
def horizontal_scroll2(self):
self.viewer.horizontalScrollBar().setValue(self.viewer2.horizontalScrollBar().value())
def vertical_scroll(self):
self.viewer2.verticalScrollBar().setValue(self.viewer.verticalScrollBar().value())
def vertical_scroll2(self):
self.viewer.verticalScrollBar().setValue(self.viewer2.verticalScrollBar().value())
def loadImage(self):
self.viewer.setPhoto(QtGui.QPixmap(r'temp.png'))
self.viewer2.setPhoto(QtGui.QPixmap(r'temp.png'))
self.image=QPixmap(r"image.jpg")
def drawmode(self):
self.viewer.toggleDrawMode()
def pixInfo(self):
self.viewer.toggleDragMode()
def photoClicked(self, pos):
if self.viewer.dragMode() == QtWidgets.QGraphicsView.NoDrag:
self.editPixInfo.setText('%d, %d' % (pos.x(), pos.y()))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = ui()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
与其尝试直接缩放视图,不如为此使用信号:
将以下代码添加到 PhotoViewer
:
class PhotoViewer(QtWidgets.QGraphicsView):
scaled = QtCore.pyqtSignal(int, int, QtGui.QTransform, int)
....
def scale(self, horz, vert):
super().scale(horz, vert)
self.scaled.emit(self.horizontalScrollBar().value(),
self.verticalScrollBar().value(),
self.transform(),
self._zoom
)
def set_transform(self, horz_scroll, vert_scroll, transform, zoom):
# temporary block signals from scroll bars to prevent interference
horz_blocked = self.horizontalScrollBar().blockSignals(True)
vert_blocked = self.verticalScrollBar().blockSignals(True)
self._zoom = zoom
self.setTransform(transform)
dx = horz_scroll - self.horizontalScrollBar().value()
dy = vert_scroll - self.verticalScrollBar().value()
self.horizontalScrollBar().setValue(dx)
self.verticalScrollBar().setValue(dy)
self.horizontalScrollBar().blockSignals(horz_blocked)
self.verticalScrollBar().blockSignals(vert_blocked)
然后 ui
:
class ui(QtWidgets.QWidget):
def __init__(self):
....
self.viewer.scaled.connect(self.viewer2.set_transform)
self.viewer2.scaled.connect(self.viewer.set_transform)
同时删除对 ui.test()
表单 PhotoViewer.wheelEvent
的调用。