pythonrotationpyqt5qgraphicspixmapitem

PyQt5 QGraphicsPixmapItem center of rotation


I am trying to make a simple speedometer using PyQt5 and Qt Designer. I have a problem due to the rotation of the speedometer arrow self.arrow.
This is the code:

class MyWin(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.speedSlider.valueChanged.connect(self.valuechange)

        scene = QGraphicsScene()
        scene.setSceneRect(0, 0, self.width(), self.height())
        self.pixmap = QPixmap(":/img/img/speed_arrow.png")
        self.arrow = scene.addPixmap(self.pixmap)

        graphicsView = QGraphicsView(scene, self.ui.centralwidget)
        graphicsView.setStyleSheet( "background-image:url(:/img/img/central1.png);\n"
                                    "background-repeat:no-repeat;\n"
                                    "background-position: center;\n"
                                    "\n"
                                    "border:none;")
        self.ui.gridLayout.addWidget(graphicsView)

    def valuechange(self):
        angel = self.ui.speedSlider.value()
        xform = QTransform()
        xform.rotate(angel)
        xformed_pixmap = self.pixmap.transformed(xform, Qt.SmoothTransformation)
        self.arrow.setPixmap(xformed_pixmap)

        #print(self.arrow.sceneBoundingRect())

Instead of drawing a speedometer arrow in the code itself, I loaded a picture.

I also added a slider self.ui.speedSlider with which the angle of rotation should be determined.

When I move the slider, the rotation of the arrow is actually achieved. The problem is that the arrow does not rotate around its center.

Example

How to fix the center of rotation so that it is always in the center of the arrow self.arrow?

This is the Ui_MainWindow code:

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        MainWindow.setStyleSheet("background-image:url(:/img/img/bgd.png);\n"
"possition:center;")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.speedSlider = QtWidgets.QSlider(self.centralwidget)
        self.speedSlider.setMaximum(99)
        self.speedSlider.setOrientation(QtCore.Qt.Horizontal)
        self.speedSlider.setObjectName("speedSlider")
        self.gridLayout.addWidget(self.speedSlider, 1, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

Arrow

Central1


Solution

  • Don't transform the pixmap whenever the value changes. Transforming the pixmap happens in the local pixmap coordinate system and only complicates things.

    Instead, set the rotation on self.arrow.

    1. Use self.arrow.setTransformOriginPoint() to set the origin of the rotation to the center of the needle's base (so almost the width of your pixmap, and half its height). Do this directly after creating the arrow.
    2. Use self.arrow.setRotation() whenever the value changes. It will rotate the arrow item around its origin point which is the needle's base.

    More information about transforms for QGraphicsItems can be found here.