QGraphicsObject 平滑旋转

问题描述 投票:0回答:1
    from PySide6.QtCore import QRect, QPoint, QRectF, Qt
    from PySide6.QtGui import QMouseEvent, QBrush, QColor, QPen, QTransform, QGuiApplication
    from PySide6.QtWidgets import QGraphicsObject, QGraphicsItem
    
    
    class ImageBox(QGraphicsObject):
        def __init__(self, pixmap):
            super().__init__()
            self.image = pixmap
            self.mousePressPos = None
            self.mousePressRect = None
            self.setAcceptHoverEvents(True)
            self.setFlag(QGraphicsItem.ItemIsMovable, True)
            self.setFlag(QGraphicsItem.ItemIsSelectable, True)
            self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
    
            self.isMirroredHorizontally = False
            self.isMirroredVertically = False
            self.rotate_op = False
    
            self.rect = self.image.rect()
    
            self.moving = []
            self.origin = QPoint()
    
            self.setTransformOriginPoint(self.rect.center())
    
        def getPixmap(self):
            return self.image
    
        def mirrorHorizontally(self):
            rect = self.boundingRect()
            self.isMirroredHorizontally = not self.isMirroredHorizontally
            scaleX = -1 if self.isMirroredHorizontally else 1
            scaleY = -1 if self.isMirroredVertically else 1
            transform = QTransform(scaleX, 0, 0, scaleY, 0, 0)
            self.setTransform(transform)
            if self.isMirroredHorizontally:
                self.moveBy(rect.width(), 0)
            else:
                self.moveBy(-rect.width(), 0)
    
        def mirrorVertically(self):
            rect = self.boundingRect()
            self.isMirroredVertically = not self.isMirroredVertically
            scaleX = -1 if self.isMirroredHorizontally else 1
            scaleY = -1 if self.isMirroredVertically else 1
            transform = QTransform(scaleX, 0, 0, scaleY, 0, 0)
            self.setTransform(transform)
            if self.isMirroredVertically:
                self.moveBy(0, rect.height())
            else:
                self.moveBy(0, -rect.height())
    
        def corners_rect(self) -> list:
            """ Return corner rect geometry for each corner"""
            size = 10  # Розмір елементів масштабування
            size2 = self.rect.width() - 40  # Ширина контейнера
            size3 = self.rect.height() - 40  # Висота контейнера
            half_size = size / 2  # Половина розміру елементів масштабування
            return [
                QRect(self.rect.left() - half_size, self.rect.top() - half_size, size, size),  # top left
                QRect(self.rect.right() - half_size, self.rect.top() - half_size, size, size),  # top right
                QRect(self.rect.left() - half_size, self.rect.bottom() - half_size, size, size),  # bottom left
                QRect(self.rect.right() - half_size, self.rect.bottom() - half_size, size, size),  # bottom right
                QRect(self.rect.left() + 20, self.rect.top() - half_size, size2, size),
                # top edge
                QRect(self.rect.right() - half_size, self.rect.top() + 20, size, size3),
                # right edge
                QRect(self.rect.left() + 20, self.rect.bottom() - half_size, size2, size),
                # bottom edge
                QRect(self.rect.left() - half_size, self.rect.top() + 20, size, size3),
                # left edge
            ]
    
        def boundingRect(self) -> QRectF:
            """ Override boundingRect """
            return self.rect.adjusted(-10, -10, 10, 10)
    
        def paint(self, painter, option, widget=None):
            painter.drawPixmap(self.rect, self.image)
    
            painter.drawRect(self.rect)
    
            point_list = self.corners_rect()
    
            if self.isSelected():
    
                pen = QPen(QColor("#00FFFF"))  # Set the color of the border
                pen.setWidth(3)  # Set the width of the border
                painter.setPen(pen)
                painter.drawRect(self.rect)
    
                painter.setBrush(QBrush(QColor("#00FFFF")))
                painter.setPen(Qt.NoPen)
                for count, rect in enumerate(point_list[:4], start=0):
                    painter.drawEllipse(rect)
    
            self.update()
    
    
        def mousePressEvent(self, event: QMouseEvent):
            """ override mouse Press Event """
            point_list = self.corners_rect()
            self.moving = [rect.contains(QPoint(event.pos().toPoint())) for rect in point_list]
            if any(self.moving):
                self.origin = self.rect.topLeft()
            else:
                super().mousePressEvent(event)
    
        def mouseReleaseEvent(self, event: QMouseEvent):
            """ Override mouse release event """
            self.moving = [False, False, False, False, False, False, False, False]
            super().mouseReleaseEvent(event)
    
        def rotateWithMouse(self, mouse_position):
            center = self.rect.center()
            angle = math.atan2(mouse_position.y() - center.y(), mouse_position.x() - center.x())
            angle = math.degrees(angle)
            self.setRotation(angle)
    
        def mouseMoveEvent(self, event: QMouseEvent):
            """ Override mouse move event """
            if any(self.moving):
                # If moving is set from mousePressEvent , change geometry
                self.prepareGeometryChange()
    
                pos = event.pos().toPoint()
    
                self.rotate_op = False
    
                if QGuiApplication.keyboardModifiers() == Qt.ShiftModifier:
                    self.rotate_op = True
                    self.rotateWithMouse(event.pos())
    
                if self.rotate_op == False:
                    if self.moving[0] or self.moving[2]:  # top left or bottom left
                        self.rect.setLeft(pos.x())
                    if self.moving[0] or self.moving[1]:  # top left or top right
                        self.rect.setTop(pos.y())
                    if self.moving[1] or self.moving[3]:  # top right or bottom right
                        self.rect.setRight(pos.x())
                    if self.moving[2] or self.moving[3]:  # bottom left or bottom right
                        self.rect.setBottom(pos.y())
    
                    if self.moving[4]:  # top edge
                        self.rect.setTop(pos.y())
                    if self.moving[6]:  # bottom edge
                        self.rect.setBottom(pos.x())
                        self.rect.setBottom(pos.y())
                    if self.moving[5]:  # right edge
                        self.rect.setRight(pos.x())
                    if self.moving[7]:  # left edge
                        self.rect.setLeft(pos.x())
    
                self.rect = self.rect.normalized()
                self.update()
                return
            else:
                super().mouseMoveEvent(event)

我有一个主类 QGraphicsView,我将图像添加为 ImageBox(QGraphicsObject) 对象。 ImageBox 有几个 QRect 点,用于调整 ImageBox 的大小。我在按下“Shift”按钮时向mouseMoveEvent方法添加了旋转,但结果是,当移动光标时,QGraphicsObject不断颤抖或将旋转返回到几毫秒前的位置,总而言之——不稳定。我想解决这个问题。

python pyqt pyside6
1个回答
0
投票

您没有考虑两个重要方面:

  1. 事件鼠标位置始终位于本地坐标(包括变换)中,因此在设置新坐标时必须始终考虑当前的
    rotation()
  2. 必须考虑矩形中心与控制点之间的角度,并从新鼠标位置的角度中减去它;
    def rotateWithMouse(self, mouse_position):
        center = self.rect.center()
        point = self.corners_rect()[self.moving.index(True)]
        pointAngle = math.degrees(math.atan2(
            point.y() - center.y(), point.x() - center.x()))
        angle = math.degrees(math.atan2(
            mouse_position.y() - center.y(), mouse_position.x() - center.x()))
        self.setRotation(self.rotation() + (angle - pointAngle))

另请注意,您需要注意以下事项:

  • 每当更改矩形几何形状时,应始终将
    transformOriginPoint
    更新到 new 中心;
  • boundingRect()
    应该始终返回 QRectF,但是您返回的是
    QImage.rect()
    ,它是一个基本(基于整数)QRect;将其更改为
    QRectF(self.rect.adjusted(-10, -10, 10, 10))
    ;
  • 您应该在更改项目的边界矩形时调用
    prepareGeometryChange()
    ,而不是在应用变换(如旋转)时调用;
  • 你应该永远不要
    update
    内调用
    paint()
    ,因为这会导致间接递归(安排不必要的重画);
© www.soinside.com 2019 - 2024. All rights reserved.