在将长方形放入QGraphicsScene并使其与鼠标移动后,我如何用鼠标调整它的大小?
如果没有人知道答案,那么一个例子也可能是好的,我正在使用PyQt4开发Python。
我知道这里也有类似的问题,但他们没有分别回答我。
我的简化代码:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
app = QApplication(sys.argv)
grview = QGraphicsView()
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 512, 512)
scene.addPixmap(QPixmap('01.png'))
grview.setScene(scene)
item = QGraphicsRectItem(0, 0, 300, 150)
pen = QPen(Qt.darkMagenta)
pen.setWidth(4)
item.setPen(pen)
item.setFlag(QGraphicsItem.ItemIsMovable)
scene.addItem(item)
grview.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
grview.show()
sys.exit(app.exec_())发布于 2015-12-23 18:58:50
您需要自己实现调整大小。为此,您需要子类QGraphicsRectItem并实现鼠标事件处理程序:mousePressEvent、mouseMoveEvent、mouseReleaseEvent。总的想法如下:
我在前几次编写了一些代码,这些代码非常适合使用PyQt5,但它也应该与PyQt4一起工作。你应该能够使它适应你的需要:
import sys
from PyQt5.QtCore import Qt, QRectF, QPointF
from PyQt5.QtGui import QBrush, QPainterPath, QPainter, QColor, QPen, QPixmap
from PyQt5.QtWidgets import QGraphicsRectItem, QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem
class GraphicsRectItem(QGraphicsRectItem):
handleTopLeft = 1
handleTopMiddle = 2
handleTopRight = 3
handleMiddleLeft = 4
handleMiddleRight = 5
handleBottomLeft = 6
handleBottomMiddle = 7
handleBottomRight = 8
handleSize = +8.0
handleSpace = -4.0
handleCursors = {
handleTopLeft: Qt.SizeFDiagCursor,
handleTopMiddle: Qt.SizeVerCursor,
handleTopRight: Qt.SizeBDiagCursor,
handleMiddleLeft: Qt.SizeHorCursor,
handleMiddleRight: Qt.SizeHorCursor,
handleBottomLeft: Qt.SizeBDiagCursor,
handleBottomMiddle: Qt.SizeVerCursor,
handleBottomRight: Qt.SizeFDiagCursor,
}
def __init__(self, *args):
"""
Initialize the shape.
"""
super().__init__(*args)
self.handles = {}
self.handleSelected = None
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.setFlag(QGraphicsItem.ItemIsFocusable, True)
self.updateHandlesPos()
def handleAt(self, point):
"""
Returns the resize handle below the given point.
"""
for k, v, in self.handles.items():
if v.contains(point):
return k
return None
def hoverMoveEvent(self, moveEvent):
"""
Executed when the mouse moves over the shape (NOT PRESSED).
"""
if self.isSelected():
handle = self.handleAt(moveEvent.pos())
cursor = Qt.ArrowCursor if handle is None else self.handleCursors[handle]
self.setCursor(cursor)
super().hoverMoveEvent(moveEvent)
def hoverLeaveEvent(self, moveEvent):
"""
Executed when the mouse leaves the shape (NOT PRESSED).
"""
self.setCursor(Qt.ArrowCursor)
super().hoverLeaveEvent(moveEvent)
def mousePressEvent(self, mouseEvent):
"""
Executed when the mouse is pressed on the item.
"""
self.handleSelected = self.handleAt(mouseEvent.pos())
if self.handleSelected:
self.mousePressPos = mouseEvent.pos()
self.mousePressRect = self.boundingRect()
super().mousePressEvent(mouseEvent)
def mouseMoveEvent(self, mouseEvent):
"""
Executed when the mouse is being moved over the item while being pressed.
"""
if self.handleSelected is not None:
self.interactiveResize(mouseEvent.pos())
else:
super().mouseMoveEvent(mouseEvent)
def mouseReleaseEvent(self, mouseEvent):
"""
Executed when the mouse is released from the item.
"""
super().mouseReleaseEvent(mouseEvent)
self.handleSelected = None
self.mousePressPos = None
self.mousePressRect = None
self.update()
def boundingRect(self):
"""
Returns the bounding rect of the shape (including the resize handles).
"""
o = self.handleSize + self.handleSpace
return self.rect().adjusted(-o, -o, o, o)
def updateHandlesPos(self):
"""
Update current resize handles according to the shape size and position.
"""
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.
"""
offset = self.handleSize + self.handleSpace
boundingRect = self.boundingRect()
rect = self.rect()
diff = QPointF(0, 0)
self.prepareGeometryChange()
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()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setLeft(toX)
boundingRect.setTop(toY)
rect.setLeft(boundingRect.left() + offset)
rect.setTop(boundingRect.top() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleTopMiddle:
fromY = self.mousePressRect.top()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setY(toY - fromY)
boundingRect.setTop(toY)
rect.setTop(boundingRect.top() + offset)
self.setRect(rect)
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()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setRight(toX)
boundingRect.setTop(toY)
rect.setRight(boundingRect.right() - offset)
rect.setTop(boundingRect.top() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleMiddleLeft:
fromX = self.mousePressRect.left()
toX = fromX + mousePos.x() - self.mousePressPos.x()
diff.setX(toX - fromX)
boundingRect.setLeft(toX)
rect.setLeft(boundingRect.left() + offset)
self.setRect(rect)
elif self.handleSelected == self.handleMiddleRight:
print("MR")
fromX = self.mousePressRect.right()
toX = fromX + mousePos.x() - self.mousePressPos.x()
diff.setX(toX - fromX)
boundingRect.setRight(toX)
rect.setRight(boundingRect.right() - offset)
self.setRect(rect)
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()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setLeft(toX)
boundingRect.setBottom(toY)
rect.setLeft(boundingRect.left() + offset)
rect.setBottom(boundingRect.bottom() - offset)
self.setRect(rect)
elif self.handleSelected == self.handleBottomMiddle:
fromY = self.mousePressRect.bottom()
toY = fromY + mousePos.y() - self.mousePressPos.y()
diff.setY(toY - fromY)
boundingRect.setBottom(toY)
rect.setBottom(boundingRect.bottom() - offset)
self.setRect(rect)
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()
diff.setX(toX - fromX)
diff.setY(toY - fromY)
boundingRect.setRight(toX)
boundingRect.setBottom(toY)
rect.setRight(boundingRect.right() - offset)
rect.setBottom(boundingRect.bottom() - offset)
self.setRect(rect)
self.updateHandlesPos()
def shape(self):
"""
Returns the shape of this item as a QPainterPath in local coordinates.
"""
path = QPainterPath()
path.addRect(self.rect())
if self.isSelected():
for shape in self.handles.values():
path.addEllipse(shape)
return path
def paint(self, painter, option, widget=None):
"""
Paint the node in the graphic view.
"""
painter.setBrush(QBrush(QColor(255, 0, 0, 100)))
painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine))
painter.drawRect(self.rect())
painter.setRenderHint(QPainter.Antialiasing)
painter.setBrush(QBrush(QColor(255, 0, 0, 255)))
painter.setPen(QPen(QColor(0, 0, 0, 255), 1.0, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
for handle, rect in self.handles.items():
if self.handleSelected is None or handle == self.handleSelected:
painter.drawEllipse(rect)
def main():
app = QApplication(sys.argv)
grview = QGraphicsView()
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 680, 459)
scene.addPixmap(QPixmap('01.png'))
grview.setScene(scene)
item = GraphicsRectItem(0, 0, 300, 150)
scene.addItem(item)
grview.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
grview.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()发布于 2021-05-09 17:08:04
丹尼莱密码非常有用,我特别喜欢调整大小的句柄。我不喜欢这样的事实,你可以把矩形移出窗口限制或调整它的大小,直到它有负的宽度或高度。
此代码强制限制移动和调整大小,因此不会导致这些问题。为了保持这个示例的简单性,我取消了调整手柄的大小。
import typing
from PySide6.QtCore import 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, *args):
super().__init__(*args)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setPen(QPen(QBrush(QColor('blue')), 5))
self.selected_edge = None
self.click_pos = self.click_rect = None
def mousePressEvent(self, event):
""" The mouse is pressed, start tracking movement. """
self.click_pos = event.pos()
rect = self.rect()
if 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):
""" 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 == '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)
# Figure out the limits of movement. I did it by updating the scene's
# rect after the window resizes.
scene_rect = self.scene().sceneRect()
view_left = scene_rect.left()
view_top = scene_rect.top()
view_right = scene_rect.right()
view_bottom = scene_rect.bottom()
# Next, check if the rectangle has been dragged out of bounds.
if rect.top() < view_top:
if self.selected_edge is None:
rect.translate(0, view_top-rect.top())
else:
rect.setTop(view_top)
if rect.left() < view_left:
if self.selected_edge is None:
rect.translate(view_left-rect.left(), 0)
else:
rect.setLeft(view_left)
if view_bottom < rect.bottom():
if self.selected_edge is None:
rect.translate(0, view_bottom - rect.bottom())
else:
rect.setBottom(view_bottom)
if view_right < rect.right():
if self.selected_edge is None:
rect.translate(view_right - rect.right(), 0)
else:
rect.setRight(view_right)
# Also check if the rectangle has been dragged inside out.
if rect.width() < 5:
if self.selected_edge == 'left':
rect.setLeft(rect.right() - 5)
else:
rect.setRight(rect.left() + 5)
if rect.height() < 5:
if self.selected_edge == 'top':
rect.setTop(rect.bottom() - 5)
else:
rect.setBottom(rect.top() + 5)
# Finally, update the rect that is now guaranteed to stay in bounds.
self.setRect(rect)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
central = QWidget(self)
self.setCentralWidget(central)
self.rect = ResizableRect()
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.old_size: typing.Optional[QSize] = None
def show(self):
super().show()
self.resize_scene()
def resizeEvent(self, event: QResizeEvent):
super().resizeEvent(event)
self.resize_scene()
def resize_scene(self):
if not self.isVisible():
# Viewport size isn't set yet, so calculation won't work.
return
size = self.view.maximumViewportSize()
if self.old_size is None:
new_rect = QRectF(size.width()/4, size.height()/4,
size.width()/2, size.height()/2)
else:
old_rect = QRectF(self.rect.rect())
x_scale = size.width() / self.old_size.width()
y_scale = size.height() / self.old_size.height()
new_rect = QRectF(old_rect.left()*x_scale, old_rect.top()*y_scale,
old_rect.width()*x_scale, old_rect.height()*y_scale)
self.rect.setRect(new_rect)
self.view.scene().setSceneRect(0, 0, size.width(), size.height())
self.old_size = size
def main():
app = QApplication()
window = MainWindow()
window.show()
app.exec_()
main()发布于 2016-06-16 20:34:06
我还试图执行类似的任务,但在我的例子中,我需要一个可重用的对象,它可以继承需要调整大小的父项。下面是一个使用此Resizer类向其父QGraphicsRectItem添加大小调整锚点的示例。
Resizer类继承QGraphicsObject并重新实现paint方法。itemChange方法Resizer发出一个信号,该信号分配给父Box类中的resize方法。这反过来使用大小调整器中的位置更改信息来调整框的rect。
通过这种方式,还可以对其他项重新搜索大小,而不必重新实现鼠标按/移动/释放事件。
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Box(QGraphicsRectItem):
def __init__(self, position, rect=QRectF(0, 0, 100, 50), parent=None, scene=None):
super().__init__(rect, parent, scene)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsFocusable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.setPos(position)
self.resizer = Resizer(parent=self)
resizerWidth = self.resizer.rect.width() / 2
resizerOffset = QPointF(resizerWidth, resizerWidth)
self.resizer.setPos(self.rect().bottomRight() - resizerOffset)
self.resizer.resizeSignal.connect(self.resize)
def paint(self, painter, option, widget=None):
pen = QPen()
pen.setColor(Qt.black)
painter.setPen(pen)
painter.setBrush(Qt.transparent)
painter.drawRect(self.rect())
@pyqtSlot()
def resize(self, change):
self.setRect(self.rect().adjusted(0, 0, change.x(), change.y()))
self.prepareGeometryChange()
self.update()
class Resizer(QGraphicsObject):
resizeSignal = pyqtSignal(QPointF)
def __init__(self, rect=QRectF(0, 0, 10, 10), parent=None):
super().__init__(parent)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.rect = rect
def boundingRect(self):
return self.rect
def paint(self, painter, option, widget=None):
if self.isSelected():
pen = QPen()
pen.setStyle(Qt.DotLine)
painter.setPen(pen)
painter.drawEllipse(self.rect)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionChange:
if self.isSelected():
self.resizeSignal.emit(value - self.pos())
return value
if __name__ == "__main__":
app = QApplication(sys.argv)
view = QGraphicsView()
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 500, 1000)
view.setScene(scene)
box = Box(QPointF(50, 50), scene=scene)
view.show()
sys.exit(app.exec_())https://stackoverflow.com/questions/34429632
复制相似问题