调整 QGraphicsSvgItem 的大小

问题描述 投票:0回答:1

我有这段代码,是我从 SO 上的一个问题中改编而来的 QGraphicsSvgItem 的代码。

问题是,当我将左侧拖动到右侧时,感觉好像右侧实际上正在向左侧移动。与从上到下相同。

简而言之,移动左侧或顶部感觉就像矩形正在反向调整大小。我认为这是因为矩形是从左上角绘制的。我无法修复它。有人可以帮助我吗?

谢谢。

import sys
from PyQt6.QtCore import Qt, QRectF, QPointF
from PyQt6.QtGui import QBrush, QPainterPath, QPainter, QColor, QPen, QTransform, QPixmap
from PyQt6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem
from PyQt6.QtSvgWidgets import QGraphicsSvgItem
from PyQt6.QtSvg import QSvgRenderer


class ResizableSvgItem(QGraphicsSvgItem):

    handleTopLeft = 1
    handleTopMiddle = 2
    handleTopRight = 3
    handleMiddleLeft = 4
    handleMiddleRight = 5
    handleBottomLeft = 6
    handleBottomMiddle = 7
    handleBottomRight = 8

    handleSize = 12.0
    handleSpace = -8.0

    handleCursors = {
        handleTopLeft: Qt.CursorShape.SizeFDiagCursor,
        handleTopMiddle: Qt.CursorShape.SizeVerCursor,
        handleTopRight: Qt.CursorShape.SizeBDiagCursor,
        handleMiddleLeft: Qt.CursorShape.SizeHorCursor,
        handleMiddleRight: Qt.CursorShape.SizeHorCursor,
        handleBottomLeft: Qt.CursorShape.SizeBDiagCursor,
        handleBottomMiddle: Qt.CursorShape.SizeVerCursor,
        handleBottomRight: Qt.CursorShape.SizeFDiagCursor,
    }

    def __init__(self, svg_file, *args):
        super().__init__(svg_file, *args)
        self.handles = {}
        self.handleSelected = None
        self.mousePressPos = None
        self.mousePressRect = None
        self.setAcceptHoverEvents(True)
        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges, True)
        self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsFocusable, True)
        self.updateHandlesPos()

    def boundingRect(self):
        """ Return the bounding rect of the SVG item. """
        # Get the original bounding rect of the SVG item
        original_rect = super().boundingRect()
        o = self.handleSize + self.handleSpace
        return original_rect.adjusted(-o, -o, o, o)

    def handleAt(self, point):
        # for k, v in self.handles.items():
        #     if v.contains(point):
        #         return k

        # Check if the point is on the border
        rect = self.boundingRect()
        border_width = self.handleSize + self.handleSpace
        if (
            point.x() >= rect.left() - border_width and point.x() <= rect.left() + border_width and
            point.y() >= rect.top() - border_width and point.y() <= rect.top() + border_width
        ):
            return self.handleTopLeft
        elif (
            point.x() >= rect.right() - border_width and point.x() <= rect.right() + border_width and
            point.y() >= rect.top() - border_width and point.y() <= rect.top() + border_width
        ):
            return self.handleTopRight
        elif (
            point.x() >= rect.right() - border_width and point.x() <= rect.right() + border_width and
            point.y() >= rect.bottom() - border_width and point.y() <= rect.bottom() + border_width
        ):
            return self.handleBottomRight
        elif (
            point.x() >= rect.left() - border_width and point.x() <= rect.left() + border_width and
            point.y() >= rect.bottom() - border_width and point.y() <= rect.bottom() + border_width
        ):
            return self.handleBottomLeft
        elif (
            point.x() >= rect.left() - border_width and point.x() <= rect.right() + border_width and
            point.y() >= rect.top() - border_width and point.y() <= rect.top() + border_width
        ):
            return self.handleTopMiddle
        elif (
            point.x() >= rect.right() - border_width and point.x() <= rect.right() + border_width and
            point.y() >= rect.top() - border_width and point.y() <= rect.bottom() + border_width
        ):
            return self.handleMiddleRight
        elif (
            point.x() >= rect.left() - border_width and point.x() <= rect.right() + border_width and
            point.y() >= rect.bottom() - border_width and point.y() <= rect.bottom() + border_width
        ):
            return self.handleBottomMiddle
        elif (
            point.x() >= rect.left() - border_width and point.x() <= rect.left() + border_width and
            point.y() >= rect.top() - border_width and point.y() <= rect.bottom() + border_width
        ):
            return self.handleMiddleLeft
        else:
            # Check if the point is inside the SVG item
            if super().contains(point):
                return None
            else:
                return self.handleTopLeft  # or any other handle, since the point is not on the border



    def hoverMoveEvent(self, moveEvent):
        if self.isSelected():
            handle = self.handleAt(moveEvent.pos())
            cursor = Qt.CursorShape.ArrowCursor if handle is None else self.handleCursors[handle]
            self.setCursor(cursor)
        super().hoverMoveEvent(moveEvent)

    def hoverLeaveEvent(self, moveEvent):
        self.setCursor(Qt.CursorShape.ArrowCursor)
        super().hoverLeaveEvent(moveEvent)

    def mousePressEvent(self, mouseEvent):
        self.handleSelected = self.handleAt(mouseEvent.pos())
        if self.handleSelected:
            self.mousePressPos = mouseEvent.pos()
            self.mousePressRect = self.boundingRect()
        super().mousePressEvent(mouseEvent)


    def mouseMoveEvent(self, mouseEvent):
        if self.handleSelected is not None:
            self.interactiveResize(mouseEvent.pos())
        else:
            super().mouseMoveEvent(mouseEvent)

    def mouseReleaseEvent(self, mouseEvent):
        super().mouseReleaseEvent(mouseEvent)
        self.handleSelected = None
        self.mousePressPos = None
        self.mousePressRect = None
        self.update()

    def updateHandlesPos(self):
        s = self.handleSize
        b = self.boundingRect()
        self.handles[self.handleTopLeft] = QRectF(b.left(), b.top(), s, s)
        self.handles[self.handleTopMiddle] = QRectF(b.center().x() - s / 2, b.top(), s, s)
        self.handles[self.handleTopRight] = QRectF(b.right() - s, b.top(), s, s)
        self.handles[self.handleMiddleLeft] = QRectF(b.left(), b.center().y() - s / 2, s, s)
        self.handles[self.handleMiddleRight] = QRectF(b.right() - s, b.center().y() - s / 2, s, s)
        self.handles[self.handleBottomLeft] = QRectF(b.left(), b.bottom() - s, s, s)
        self.handles[self.handleBottomMiddle] = QRectF(b.center().x() - s / 2, b.bottom() - s, s, s)
        self.handles[self.handleBottomRight] = QRectF(b.right() - s, b.bottom() - s, s, s)



    def interactiveResize(self, mousePos):
        """ Perform shape interactive resize. """
        boundingRect = self.boundingRect()
        rect = boundingRect

        self.prepareGeometryChange()

        if self.handleSelected is not None:
            if self.handleSelected == self.handleTopLeft:
                fromX = self.mousePressRect.left()
                fromY = self.mousePressRect.top()
                toX = fromX + mousePos.x() - self.mousePressPos.x()
                toY = fromY + mousePos.y() - self.mousePressPos.y()
                rect.setLeft(toX)
                rect.setTop(toY)

            elif self.handleSelected == self.handleTopMiddle:
                fromY = self.mousePressRect.top()
                toY = fromY + mousePos.y() - self.mousePressPos.y()
                rect.setTop(toY)

            elif self.handleSelected == self.handleTopRight:
                fromX = self.mousePressRect.right()
                fromY = self.mousePressRect.top()
                toX = fromX + mousePos.x() - self.mousePressPos.x()
                toY = fromY + mousePos.y() - self.mousePressPos.y()
                rect.setRight(toX)
                rect.setTop(toY)

            elif self.handleSelected == self.handleMiddleLeft:
                fromX = self.mousePressRect.left()
                toX = fromX + mousePos.x() - self.mousePressPos.x()
                rect.setLeft(toX)

            elif self.handleSelected == self.handleMiddleRight:
                fromX = self.mousePressRect.right()
                toX = fromX + mousePos.x() - self.mousePressPos.x()
                rect.setRight(toX)

            elif self.handleSelected == self.handleBottomLeft:
                fromX = self.mousePressRect.left()
                fromY = self.mousePressRect.bottom()
                toX = fromX + mousePos.x() - self.mousePressPos.x()
                toY = fromY + mousePos.y() - self.mousePressPos.y()
                rect.setLeft(toX)
                rect.setBottom(toY)

            elif self.handleSelected == self.handleBottomMiddle:
                fromY = self.mousePressRect.bottom()
                toY = fromY + mousePos.y() - self.mousePressPos.y()
                rect.setBottom(toY)

            elif self.handleSelected == self.handleBottomRight:
                fromX = self.mousePressRect.right()
                fromY = self.mousePressRect.bottom()
                toX = fromX + mousePos.x() - self.mousePressPos.x()
                toY = fromY + mousePos.y() - self.mousePressPos.y()
                rect.setRight(toX)
                rect.setBottom(toY)


        # Calculate the new scale
        newWidth = rect.width()
        newHeight = rect.height()
        scaleX = newWidth / self.mousePressRect.width()
        scaleY = newHeight / self.mousePressRect.height()

        # Apply the scaling transformation
        self.setTransform(QTransform().scale(scaleX, scaleY), True)

        self.updateHandlesPos()



    def shape(self):
        """ Returns the shape of this item as a QPainterPath in local coordinates. """
        path = QPainterPath()
        path.addRect(self.boundingRect())  # Use boundingRect instead of rect
        if self.isSelected():
            for shape in self.handles.values():
                path.addEllipse(shape)
        return path


    def paint(self, painter, option, widget=None):
        """ Paint the SVG item and its resize handles. """
        # Draw the SVG item
        super().paint(painter, option, widget)

        # Draw the handles
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        painter.setBrush(QBrush(QColor(255, 0, 0, 255)))
        painter.setPen(QPen(QColor(0, 0, 0, 255), 1.0, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))
        
        for handle, rect in self.handles.items():
            if self.handleSelected is None or handle == self.handleSelected:
                painter.drawEllipse(rect)
   
python-3.x pyqt pyqt6
1个回答
0
投票

终于修好了。现在调整大小可以按预期进行。

def interactiveResize(self, mousePos):
    """ Perform shape interactive resize. """
    boundingRect = self.boundingRect()
    rect = boundingRect

    self.prepareGeometryChange()

    if self.handleSelected is not None:
        dx = mousePos.x() - self.mousePressPos.x()
        dy = mousePos.y() - self.mousePressPos.y()

        if self.handleSelected == self.handleTopLeft:
            self.setTransform(QTransform().translate(dx, dy).scale(
                (self.mousePressRect.width() - dx) / self.mousePressRect.width(),
                (self.mousePressRect.height() - dy) / self.mousePressRect.height()
            ), True)
        elif self.handleSelected == self.handleTopMiddle:
            self.setTransform(QTransform().translate(0, dy).scale(
                1, (self.mousePressRect.height() - dy) / self.mousePressRect.height()
            ), True)
        elif self.handleSelected == self.handleTopRight:
            self.setTransform(QTransform().translate(0, dy).scale(
                (self.mousePressRect.width() + dx) / self.mousePressRect.width(),
                (self.mousePressRect.height() - dy) / self.mousePressRect.height()
            ), True)
        elif self.handleSelected == self.handleMiddleLeft:
            self.setTransform(QTransform().translate(dx, 0).scale(
                (self.mousePressRect.width() - dx) / self.mousePressRect.width(),
                1
            ), True)
        elif self.handleSelected == self.handleMiddleRight:
            self.setTransform(QTransform().translate(0, 0).scale(
                (self.mousePressRect.width() + dx) / self.mousePressRect.width(),
                1
            ), True)
        elif self.handleSelected == self.handleBottomLeft:
            self.setTransform(QTransform().translate(dx, 0).scale(
                (self.mousePressRect.width() - dx) / self.mousePressRect.width(),
                (self.mousePressRect.height() + dy) / self.mousePressRect.height()
            ), True)
        elif self.handleSelected == self.handleBottomMiddle:
            self.setTransform(QTransform().translate(0, 0).scale(
                1, (self.mousePressRect.height() + dy) / self.mousePressRect.height()
            ), True)
        elif self.handleSelected == self.handleBottomRight:
            self.setTransform(QTransform().translate(0, 0).scale(
                (self.mousePressRect.width() + dx) / self.mousePressRect.width(),
                (self.mousePressRect.height() + dy) / self.mousePressRect.height()
            ), True)

    self.updateHandlesPos()
© www.soinside.com 2019 - 2024. All rights reserved.