Search code examples
pythonpysideqwidgetqtguipaintevent

Pan & Zoom all QWidget children


I would like to have a QWidget which has the possiblity to pan and zoom inside it.

Scenario :

  • When the user uses his mouse middle button to scroll-up/down, all children are scaled from the center of the widget.
  • If the user holds Alt pressed, and presses mouse middle button too, all children are translated using the distance made by the mouse.

The idea is to create a new QWidget by subclassing another one. So I made it with QGraphicsView for the example :

UPDATE :

class MyQGraphicsView(QGraphicsView):
    def __init__(self):
        QGraphicsView.__init__(self)
        self.setFocusPolicy(Qt.WheelFocus)
        self.setRenderHints(QPainter.Antialiasing)
        self.altPressed = False
        self.middlePressed = False
        self.rightPressed = False

    def wheelEvent(self, event):
        self.newScale(event.delta(), 1.15)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Alt:
            self.altPressed = True
            QApplication.setOverrideCursor(Qt.OpenHandCursor)

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Alt:
            self.altPressed = False
            QApplication.setOverrideCursor(Qt.ArrowCursor)

    def mousePressEvent(self, event):
        self._dragPos = event.pos()
        if event.button() == Qt.MidButton:
            self.middlePressed = True
        if event.button() == Qt.RightButton:
            self.rightPressed = True

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.MidButton:
            self.middlePressed = False
        if event.button() == Qt.RightButton:
            self.rightPressed = False

    def mouseMoveEvent(self, event):
        if self.altPressed:
            newPos = event.pos()

            if self.middlePressed:
                diff = newPos - self._dragPos
                self._dragPos = newPos
                QApplication.setOverrideCursor(Qt.ClosedHandCursor)
                self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - diff.x())
                self.verticalScrollBar().setValue(self.verticalScrollBar().value() - diff.y())
                event.accept()
            if self.rightPressed:
                diff = newPos - self._dragPos
                self._dragPos = newPos
                QApplication.setOverrideCursor(Qt.SizeAllCursor)
                self.newScale(diff.x(), 1.01)

    def newScale(self, operator, factor):
        if operator > 0:
            self.scale(factor, factor)
        if operator < 0:
            self.scale(1.0/factor, 1.0/factor)

This class works fine for panning and zooming, but when I use it like this :

view = MyQGraphicsView()
scene = QGraphicsScene()
view.setScene(scene)
view.setSceneRect(0, 0, 1000, 1000)

button = QPushButton('Hello !')
button.clicked.connect(self.sayHello)
scene.addWidget(button)

I can't click on the button. How can I resolve it ?


Solution

  • Finally, I just forgot to add a super constructor on my overridden functions, ex :

    super(MyQGraphicsView, self).mouseMoveEvent(event)