How to enable Pan and Zoom in a QGraphicsView

前端 未结 2 1090
被撕碎了的回忆
被撕碎了的回忆 2020-11-27 03:20

I am using python and Qt Designer to implement loading tiff images and to enable Pan and Zoom on some mouse event (wheel - zoom, press wheel - pan).

I was looking in

相关标签:
2条回答
  • 2020-11-27 03:51

    This is not too difficult to do using the built in capabilities of QGraphicsView.

    The demo script below has left-button panning and wheel zoom (including anchoring to the current cursor position). The fitInView method has been reimplemented because the built in version adds a weird fixed margin that can't be removed.

    PyQt4 version:

    from PyQt4 import QtCore, QtGui
    
    class PhotoViewer(QtGui.QGraphicsView):
        photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
    
        def __init__(self, parent):
            super(PhotoViewer, self).__init__(parent)
            self._zoom = 0
            self._empty = True
            self._scene = QtGui.QGraphicsScene(self)
            self._photo = QtGui.QGraphicsPixmapItem()
            self._scene.addItem(self._photo)
            self.setScene(self._scene)
            self.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
            self.setResizeAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
            self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
            self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
            self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
            self.setFrameShape(QtGui.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(QtGui.QGraphicsView.ScrollHandDrag)
                self._photo.setPixmap(pixmap)
            else:
                self._empty = True
                self.setDragMode(QtGui.QGraphicsView.NoDrag)
                self._photo.setPixmap(QtGui.QPixmap())
            self.fitInView()
    
        def wheelEvent(self, event):
            if self.hasPhoto():
                if event.delta() > 0:
                    factor = 1.25
                    self._zoom += 1
                else:
                    factor = 0.8
                    self._zoom -= 1
                if self._zoom > 0:
                    self.scale(factor, factor)
                elif self._zoom == 0:
                    self.fitInView()
                else:
                    self._zoom = 0
    
        def toggleDragMode(self):
            if self.dragMode() == QtGui.QGraphicsView.ScrollHandDrag:
                self.setDragMode(QtGui.QGraphicsView.NoDrag)
            elif not self._photo.pixmap().isNull():
                self.setDragMode(QtGui.QGraphicsView.ScrollHandDrag)
    
        def mousePressEvent(self, event):
            if self._photo.isUnderMouse():
                self.photoClicked.emit(self.mapToScene(event.pos()).toPoint())
            super(PhotoViewer, self).mousePressEvent(event)
    
    
    class Window(QtGui.QWidget):
        def __init__(self):
            super(Window, self).__init__()
            self.viewer = PhotoViewer(self)
            # 'Load image' button
            self.btnLoad = QtGui.QToolButton(self)
            self.btnLoad.setText('Load image')
            self.btnLoad.clicked.connect(self.loadImage)
            # Button to change from drag/pan to getting pixel info
            self.btnPixInfo = QtGui.QToolButton(self)
            self.btnPixInfo.setText('Enter pixel info mode')
            self.btnPixInfo.clicked.connect(self.pixInfo)
            self.editPixInfo = QtGui.QLineEdit(self)
            self.editPixInfo.setReadOnly(True)
            self.viewer.photoClicked.connect(self.photoClicked)
            # Arrange layout
            VBlayout = QtGui.QVBoxLayout(self)
            VBlayout.addWidget(self.viewer)
            HBlayout = QtGui.QHBoxLayout()
            HBlayout.setAlignment(QtCore.Qt.AlignLeft)
            HBlayout.addWidget(self.btnLoad)
            HBlayout.addWidget(self.btnPixInfo)
            HBlayout.addWidget(self.editPixInfo)
            VBlayout.addLayout(HBlayout)
    
        def loadImage(self):
            self.viewer.setPhoto(QtGui.QPixmap('image.jpg'))
    
        def pixInfo(self):
            self.viewer.toggleDragMode()
    
        def photoClicked(self, pos):
            if self.viewer.dragMode()  == QtGui.QGraphicsView.NoDrag:
                self.editPixInfo.setText('%d, %d' % (pos.x(), pos.y()))
    
    
    if __name__ == '__main__':
        import sys
        app = QtGui.QApplication(sys.argv)
        window = Window()
        window.setGeometry(500, 300, 800, 600)
        window.show()
        sys.exit(app.exec_())
    

    PyQt5 version:

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class PhotoViewer(QtWidgets.QGraphicsView):
        photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
    
        def __init__(self, parent):
            super(PhotoViewer, self).__init__(parent)
            self._zoom = 0
            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:
                    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)
    
        def mousePressEvent(self, event):
            if self._photo.isUnderMouse():
                self.photoClicked.emit(self.mapToScene(event.pos()).toPoint())
            super(PhotoViewer, self).mousePressEvent(event)
    
    
    class Window(QtWidgets.QWidget):
        def __init__(self):
            super(Window, self).__init__()
            self.viewer = PhotoViewer(self)
            # 'Load image' button
            self.btnLoad = QtWidgets.QToolButton(self)
            self.btnLoad.setText('Load image')
            self.btnLoad.clicked.connect(self.loadImage)
            # 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)
            VBlayout.addWidget(self.viewer)
            HBlayout = QtWidgets.QHBoxLayout()
            HBlayout.setAlignment(QtCore.Qt.AlignLeft)
            HBlayout.addWidget(self.btnLoad)
            HBlayout.addWidget(self.btnPixInfo)
            HBlayout.addWidget(self.editPixInfo)
            VBlayout.addLayout(HBlayout)
    
        def loadImage(self):
            self.viewer.setPhoto(QtGui.QPixmap('image.jpg'))
    
        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__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        window = Window()
        window.setGeometry(500, 300, 800, 600)
        window.show()
        sys.exit(app.exec_())
    
    0 讨论(0)
  • 2020-11-27 03:57

    It is possible to open TIFF files up to several gigabytes with ordinary PIL (pillow) library. It is not quite easy, but it works.

    You could see the example here, second example after bold EDIT string could open, move and zoom TIFF files.

    0 讨论(0)
提交回复
热议问题