效果

说明
- 悬浮效果通过重写hoverMoveEvent实现,若鼠标在边框附近则修改鼠标样式,离开边框区域则恢复。
- 移动和缩放怎么区分,鼠标按下去的时候,如果鼠标在边框附近,则是缩放,如果不在边框附近,则是移动。
- 方法中有个代表图元位置和大小的Rect,操作通过adjust()/translate()修改到Rect,通过setRect()更新到图元中。
代码
import typing
from PySide6.QtCore import Qt, QRectF, QSize
from PySide6.QtGui import QPen, QBrush, QColor, QResizeEvent
from PySide6.QtWidgets import (QApplication, QGraphicsView, QGraphicsScene,
QGraphicsItem, QGraphicsRectItem, QMainWindow,
QVBoxLayout, QWidget)
class ResizableRect(QGraphicsRectItem):
def __init__(self,
rectSize = QRectF(0,0,100,200),
rectPen = QPen(QBrush(QColor('blue')), 5)
):
super().__init__()
self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable, True)
self.setPen(rectPen)
self.setRect(rectSize)
self.setAcceptHoverEvents(True)
self.selected_edge = None
#self.hover_edge = None
self.click_pos = self.click_rect = None
def mousePressEvent(self, event):
""" The mouse is pressed, start tracking movement. """
print('mousePress')
self.click_pos = event.pos()
rect = self.rect()
if abs(rect.top() - self.click_pos.y()) < 5 and abs(rect.left() - self.click_pos.x()) < 5:
self.selected_edge = 'topleft'
elif abs(rect.top() - self.click_pos.y()) < 5 and abs(rect.right() - self.click_pos.x()) < 5:
self.selected_edge = 'topright'
elif abs(rect.bottom() - self.click_pos.y()) < 5 and abs(rect.left() - self.click_pos.x()) < 5:
self.selected_edge = 'bottomleft'
elif abs(rect.bottom() - self.click_pos.y()) < 5 and abs(rect.right() - self.click_pos.x()) < 5:
self.selected_edge = 'bottomright'
elif abs(rect.left() - self.click_pos.x()) < 5:
self.selected_edge = 'left'
elif abs(rect.right() - self.click_pos.x()) < 5:
self.selected_edge = 'right'
elif abs(rect.top() - self.click_pos.y()) < 5:
self.selected_edge = 'top'
elif abs(rect.bottom() - self.click_pos.y()) < 5:
self.selected_edge = 'bottom'
else:
self.selected_edge = None
self.click_pos = event.pos()
self.click_rect = rect
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
#如果已经设置标志不可移动,则直接返回
flags = self.flags()
if ((flags & QGraphicsItem.GraphicsItemFlag.ItemIsMovable) != QGraphicsItem.GraphicsItemFlag.ItemIsMovable):
return super().mouseMoveEvent(event)
""" Continue tracking movement while the mouse is pressed. """
# Calculate how much the mouse has moved since the click.
pos = event.pos()
x_diff = pos.x() - self.click_pos.x()
y_diff = pos.y() - self.click_pos.y()
# Start with the rectangle as it was when clicked.
rect = QRectF(self.click_rect)
# Then adjust by the distance the mouse moved.
if self.selected_edge is None:
rect.translate(x_diff, y_diff)
elif self.selected_edge == 'topleft':
rect.adjust(x_diff, y_diff, 0, 0)
elif self.selected_edge == 'topright':
rect.adjust(0, y_diff, x_diff, 0)
elif self.selected_edge == 'bottomleft':
rect.adjust(x_diff, 0, 0, y_diff)
elif self.selected_edge == 'bottomright':
rect.adjust(0, 0, x_diff, y_diff)
elif self.selected_edge == 'top':
rect.adjust(0, y_diff, 0, 0)
elif self.selected_edge == 'left':
rect.adjust(x_diff, 0, 0, 0)
elif self.selected_edge == 'bottom':
rect.adjust(0, 0, 0, y_diff)
elif self.selected_edge == 'right':
rect.adjust(0, 0, x_diff, 0)
print(self.selected_edge)
# Also check if the rectangle has been dragged inside out.
if rect.width() < 5:
if self.selected_edge == 'left' or self.selected_edge == 'topleft' or self.selected_edge == "bottomleft":
rect.setLeft(rect.right() - 5)
elif self.selected_edge == 'right' or self.selected_edge == "topright" or self.selected_edge == "bottomright":
rect.setRight(rect.left() + 5)
if rect.height() < 5:
if self.selected_edge == 'top' or self.selected_edge == "topleft" or self.selected_edge == "topright":
rect.setTop(rect.bottom() - 5)
elif self.selected_edge == 'bottom' or self.selected_edge == "bottomleft" or self.selected_edge == "bottomright":
rect.setBottom(rect.top() + 5)
# Finally, update the rect that is now guaranteed to stay in bounds.
self.setRect(rect)
#return super().mouseMoveEvent(event) 这句话不能加,不然失去变形效果
def hoverMoveEvent(self, event):
print('hoverMove')
self.hover_pos = event.pos()
rect = self.rect()
cursor_shape = Qt.CursorShape.ArrowCursor
#corner is not need to be hovered
if abs(rect.left() - self.hover_pos.x()) < 5 and abs(rect.top() - self.hover_pos.y()) < 5:
cursor_shape = Qt.CursorShape.SizeFDiagCursor
elif abs(rect.right() - self.hover_pos.x()) < 5 and abs(rect.top() - self.hover_pos.y()) < 5:
cursor_shape = Qt.CursorShape.SizeBDiagCursor
elif abs(rect.left() - self.hover_pos.x()) < 5 and abs(rect.bottom() - self.hover_pos.y()) < 5:
cursor_shape = Qt.CursorShape.SizeBDiagCursor
elif abs(rect.right() - self.hover_pos.x()) < 5 and abs(rect.bottom() - self.hover_pos.y()) < 5:
cursor_shape = Qt.CursorShape.SizeFDiagCursor
elif abs(rect.left() - self.hover_pos.x()) < 5:
cursor_shape = Qt.CursorShape.SizeHorCursor
elif abs(rect.right() - self.hover_pos.x()) < 5:
cursor_shape = Qt.CursorShape.SizeHorCursor
elif abs(rect.top() - self.hover_pos.y()) < 5:
cursor_shape = Qt.CursorShape.SizeVerCursor
elif abs(rect.bottom() - self.hover_pos.y()) < 5:
cursor_shape = Qt.CursorShape.SizeVerCursor
else:
#self.selected_edge = None
cursor_shape = Qt.CursorShape.ArrowCursor
self.setCursor(cursor_shape)
return super().hoverMoveEvent(event)
def hoverLeaveEvent(self, event):
print('hoverLeave')
#self.selected_edge = None
self.setCursor(Qt.CursorShape.ArrowCursor)
return super().hoverLeaveEvent(event)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
central = QWidget(self)
self.setCentralWidget(central)
self.rect = ResizableRect(rectSize=QRectF(30, 40,100, 70), rectPen=QPen(QBrush(QColor('red')), 5))
scene = QGraphicsScene(0, 0, 300, 300)
scene.addItem(self.rect)
self.view = QGraphicsView(central)
self.view.setScene(scene)
layout = QVBoxLayout(central)
self.setLayout(layout)
layout.addWidget(self.view)
#self.rect.setRect(QRectF(30, 40,100, 70))
def main():
app = QApplication()
window = MainWindow()
window.show()
app.exec_()
main()