简体   繁体   中英

QGraphicsPixmapItem is not rotating correctly

I need to rotate a QGraphicsPixmapItem through a circle. That is, the circle always needs to be at the top left corner of the image and when I drag the circle, the image has to rotate. I set the rotation angle using setRotation and the rotation point with setTransformOriginPoint . However, the rotation is not working fine. Could someone point me in the right direction please?

import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QGraphicsView
from PyQt5 import QtGui, QtWidgets, QtCore
import math

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.scene = Scene()
        self.view = QGraphicsView(self)
        self.setGeometry(10, 30, 850, 600)
        self.view.setGeometry(20, 22, 800, 550)
        self.setFixedSize(850, 600)
        self.view.setScene(self.scene)

class Scene(QtWidgets.QGraphicsScene):
    def __init__(self, parent=None):
        super(Scene, self).__init__(parent)
        # other stuff here
        self.set_image()

    def set_image(self):
        image = Image()
        self.addItem(image)
        image.set_pixmap()

class Image(QtWidgets.QGraphicsPixmapItem):
    def __init__(self, parent=None):
        super(Image, self).__init__(parent)

        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
        self.setTransformOriginPoint(self.boundingRect().center())

    def set_pixmap(self):
        pixmap = QtGui.QPixmap("image.jpg")
        self.setPixmap(pixmap)
        self.pixmap_controller = PixmapController(self)
        self.pixmap_controller.set_pixmap_controller()

class PixmapController(QtWidgets.QGraphicsEllipseItem):
    def __init__(self, pixmap):
        super(PixmapController, self).__init__(parent=pixmap)
        self.pixmap = pixmap

        color = QtGui.QColor(0, 0, 0)
        brush = QtGui.QBrush(color)
        self.setBrush(brush)

    def set_pixmap_controller(self):
        self.setRect(-5, -5, 10, 10)

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self.startPos = event.pos()

    def mouseMoveEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton:
            self.finalPos = event.pos()
            delta = self.finalPos - self.startPos
            item_position = self.pixmap.transformOriginPoint()
            angle = math.atan2(item_position.y() - self.finalPos.y(), item_position.x() - self.finalPos.x()) / math.pi * 180 - 45
            self.parentItem().setRotation(angle)
            self.parentItem().setPos(self.parentItem().pos() + delta)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

As with your previous question, you have to consider that the item coordinate system is based on its parent (or the scene, if there's none).

There are also two problems with your code:

  1. you are setting the transformation origin point when the item has no pixmap set, so that point will be null ( QPointF() , as in the top left corner of the item) as the bounding rect is null at that time; this will make the transformation inconsistent, as the origin will always be on the top left of the pixmap item, not its center;
  2. you are trying to do both rotation and translation within the same mouse movements and those transformations are obviously conceptually incompatible. The concept of rotation is that it's a circular movement around a center, which is fixed in the coordinate system of the rotation . While you can clearly have both rotation and translation of an object, with a singular reference point for the transformation you cannot have both of them;

If you still want to be able to do both transformations individually using mouse movements, you can use the event modifiers to choose which transformation actually apply. In the following example, the normal mouse movement causes translation, while keeping pressed the Ctrl key when clicking will apply a rotation.

class Image(QtWidgets.QGraphicsPixmapItem):
    # ...
    def set_pixmap(self):
        pixmap = QtGui.QPixmap("image.jpg")
        self.setPixmap(pixmap)
        self.pixmap_controller = PixmapController(self)
        self.pixmap_controller.set_pixmap_controller()
        


class PixmapController(QtWidgets.QGraphicsEllipseItem):
    # ...
    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self.startPos = event.pos()
            self.origin = self.parentItem().transformOriginPoint()
            self.startAngle = math.atan2(
                    self.origin.y(), 
                    self.origin.x()
                ) / math.pi * 180 - 45
            self.isRotating = event.modifiers() == QtCore.Qt.ControlModifier

    def mouseMoveEvent(self, event):
        if event.buttons() == QtCore.Qt.LeftButton:
            self.finalPos = event.pos()
            delta = self.finalPos - self.startPos
            angle = math.atan2(
                    self.origin.y() - delta.y(), 
                    self.origin.x() - delta.x()
                ) / math.pi * 180 - 45
            if self.isRotating:
                self.parentItem().setRotation(
                    self.parentItem().rotation() + (angle - self.startAngle))
            else:
                self.parentItem().setPos(self.parentItem().pos() + delta)

A small suggestion: functions should always be created for their reusability and/or readability of the code; since you're always creating the same rectangle for the ellipse item (and that rectangle is always based on the parent's coordinate system), there's no use for a dedicate function like set_pixmap_controller and its related call, just use setRect() in the __init__ .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM