链接两个 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 的调用。