Search code examples
pythonpython-3.xpyqtpyqt5qpainter

QPaint translation scale with mouseevent


I need to draw a shape with scaling and Qwheel event. I have tried painter.scale and painter.translate, but it does not at all effect the scaling of shape.

The idea is to draw different shapes, for instance this one on the figure below, a rectangular shape with start value ( 200, 300) looks perfect and even centered at canvas widget. but when with greater value ( 500, 700 ) as shown on second below figure enlarges and becomes unwanted. I want to draw it with scale and with ability to wheel (zooming in and out) it with mouse event.

Visualization.

enter image description here

enter image description here

The code:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class Foo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Foo, self).__init__(parent)
        self.setGeometry(QtCore.QRect(200, 100, 700, 600))        
        self.paint = Paint()
        self.sizeHint()
        self.lay = QtWidgets.QVBoxLayout()
        self.lay.addWidget(self.paint)
        self.setLayout(self.lay)

class Paint(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Paint, self).__init__(parent)
        self.setBackgroundRole(QtGui.QPalette.Base)     
        self.setAutoFillBackground(True)
        self._width = 200
        self._height = 300


    def paintEvent(self, event):
        pen = QtGui.QPen()
        brush = QtGui.QBrush( QtCore.Qt.darkCyan, QtCore.Qt.Dense5Pattern)
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.setPen(pen)
        painter.setBrush(brush)
        r = QtCore.QRect(QtCore.QPoint((self.width() - self._width)/ 2, (self.height() -self._height)/ 2), QtCore.QSize(self._width, self._height))
        painter.drawRect(r)
        painter.scale(0.4,0.4)
        painter.translate(50, 50)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = Foo()
    w.show()
    sys.exit(app.exec_())

I appreciate muchhh any help. Thanks


Solution

  • The scale is given with respect to the top-left so if we want it to scale with respect to the center it must first be moved there, and after scaling it is placed in the initial position. For the case of the scale factor, use the wheelEvent method using the angleDelta(), multiplying by an appropriate factor.

    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class Foo(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(Foo, self).__init__(parent)
            self.setGeometry(QtCore.QRect(200, 100, 700, 600))        
            self.paint = Paint()
            self.sizeHint()
            self.lay = QtWidgets.QVBoxLayout()
            self.lay.addWidget(self.paint)
            self.setLayout(self.lay)
    
    class Paint(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(Paint, self).__init__(parent)
            self.setBackgroundRole(QtGui.QPalette.Base)     
            self.setAutoFillBackground(True)
            self.r = QtCore.QRect(QtCore.QPoint(), QtCore.QSize(200, 300))
            self._factor = 1.0
    
        def paintEvent(self, event):
            self.r.moveCenter(self.rect().center())
            pen = QtGui.QPen()
            brush = QtGui.QBrush( QtCore.Qt.darkCyan, QtCore.Qt.Dense5Pattern)
            painter = QtGui.QPainter(self)
            painter.setBrush(brush)
            painter.setPen(pen)
            painter.setRenderHint(QtGui.QPainter.Antialiasing)
    
            painter.translate(self.rect().center())
            painter.scale(self._factor, self._factor)
            painter.translate(-self.rect().center())
    
            painter.drawRect(self.r)
    
        def wheelEvent(self, event):
            self._factor *= 1.01**(event.angleDelta().y()/15.0)
            self.update()
            super(Paint, self).wheelEvent(event)
    
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
        w = Foo()
        w.show()
        sys.exit(app.exec_())
    

    enter image description here

    enter image description here

    enter image description here