QGraphicsPixmapItem无法正确旋转

问题描述

我需要将QGraphicspixmapItem旋转一圈。也就是说,圆圈必须始终位于图像的左上角,并且当我拖动圆圈时,图像必须旋转。我使用setRotation设置旋转角度,并使用setTransformOriginPoint设置旋转点。但是,旋转效果不佳。有人可以指出我正确的方向吗?

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)
        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_())

解决方法

与上一个问题一样,您必须考虑项目坐标系基于其父级(如果没有,则基于场景)。

您的代码还有两个问题:

  1. 您要在没有设置像素图的情况下设置转换原点,因此该点将为空(QPointF(),如该项目的左上角),因为此时边界rect为空;这将使转换不一致,因为原点始终位于像素图项的左上方,而不是其中心;
  2. 您试图在相同的鼠标移动中同时进行旋转,并且这些转换在概念上显然不兼容。旋转的概念是绕中心的圆周运动,它固定在旋转的坐标系 中。虽然您可以清楚地使对象具有旋转平移,但只有一个转换的唯一参考点,但是您不能同时拥有它们;

如果您仍然希望能够使用鼠标移动分别进行这两种转换,则可以使用事件修饰符选择实际应用的转换。在下面的示例中,正常的鼠标移动会引起平移,而在单击时,按住 Ctrl 键将进行旋转。

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()
        self.setTransformOriginPoint(self.boundingRect().center())


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)

一个小建议:应始终出于功能的可重用性和/或代码的可读性而创建函数;由于您始终为椭圆项创建相同的矩形(并且该矩形始终基于父项的坐标系),因此像set_pixmap_controller之类的专用函数及其相关调用没有用,只需使用{{1 }}中的setRect()