我有这段代码,是我从 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)
终于修好了。现在调整大小可以按预期进行。
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()