在与鼠标速度成比例的 QGraphicsItem 中心调整大小?
Resize about center on a QGraphicsItem proportional to mouse speed?
很抱歉重复询问有关 pyqt 的问题,但有限的资源迫使我这样做。
我一直在尝试为我的圆形实现一个调整大小的功能,这是 QGraphicsItem 的扩展,它有一个椭圆和一个居中的文本。我可以根据需要调整我的形状,但形状需要一段时间才能赶上鼠标,即在切换方向时,圆圈继续增加但需要一段时间才能切换方向,此外,圆圈的大小会随着顶部的锚点而调整边界矩形的左角。
请参阅 了解一些代码背景
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):
self.prepareGeometryChange()
if self.handleSelected in [self.handleTopLeft,
self.handleTopRight,
self.handleBottomLeft,
self.handleBottomRight,
self.handleTopMiddle,
self.handleBottomMiddle,
self.handleMiddleLeft,
self.handleMiddleRight]:
self.radius += (mousePos.y() + mousePos.x() + self.mousePressPos.x() - self.mousePressPos.y())/64
self.setPos(self.x(),self.y())
self.update()
self.updateHandlesPos()
由于OP没有提供MRE,所以我从头开始创建了一个例子。其逻辑是跟踪item的变化,并据此计算出新的geometry,建立其他item的新位置。
from PyQt5 import QtWidgets, QtGui, QtCore
class GripItem(QtWidgets.QGraphicsPathItem):
circle = QtGui.QPainterPath()
circle.addEllipse(QtCore.QRectF(-5, -5, 10, 10))
square = QtGui.QPainterPath()
square.addRect(QtCore.QRectF(-10, -10, 20, 20))
def __init__(self, annotation_item, index):
super(GripItem, self).__init__()
self.m_annotation_item = annotation_item
self.m_index = index
self.setPath(GripItem.circle)
self.setBrush(QtGui.QColor("green"))
self.setPen(QtGui.QPen(QtGui.QColor("green"), 2))
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
self.setAcceptHoverEvents(True)
self.setZValue(11)
self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
def hoverEnterEvent(self, event):
self.setPath(GripItem.square)
self.setBrush(QtGui.QColor("red"))
super(GripItem, self).hoverEnterEvent(event)
def hoverLeaveEvent(self, event):
self.setPath(GripItem.circle)
self.setBrush(QtGui.QColor("green"))
super(GripItem, self).hoverLeaveEvent(event)
def mouseReleaseEvent(self, event):
self.setSelected(False)
super(GripItem, self).mouseReleaseEvent(event)
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
self.m_annotation_item.movePoint(self.m_index, value)
return super(GripItem, self).itemChange(change, value)
class DirectionGripItem(GripItem):
def __init__(self, annotation_item, direction=QtCore.Qt.Horizontal, parent=None):
super(DirectionGripItem, self).__init__(annotation_item, parent)
self._direction = direction
@property
def direction(self):
return self._direction
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
p = QtCore.QPointF(self.pos())
if self.direction == QtCore.Qt.Horizontal:
p.setX(value.x())
elif self.direction == QtCore.Qt.Vertical:
p.setY(value.y())
self.m_annotation_item.movePoint(self.m_index, p)
return p
return super(DirectionGripItem, self).itemChange(change, value)
class CircleAnnotation(QtWidgets.QGraphicsEllipseItem):
def __init__(self, radius=1, parent=None):
super(CircleAnnotation, self).__init__(parent)
self.setZValue(11)
self.m_items = []
self.setPen(QtGui.QPen(QtGui.QColor("green"), 4))
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
self.setAcceptHoverEvents(True)
self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
self._radius = radius
self.update_rect()
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, r):
if r <= 0:
raise ValueError("radius must be positive")
self._radius = r
self.update_rect()
self.add_grip_items()
self.update_items_positions()
def update_rect(self):
rect = QtCore.QRectF(0, 0, 2 * self.radius, 2 * self.radius)
rect.moveCenter(self.rect().center())
self.setRect(rect)
def add_grip_items(self):
if self.scene() and not self.m_items:
for i, (direction) in enumerate(
(
QtCore.Qt.Vertical,
QtCore.Qt.Horizontal,
QtCore.Qt.Vertical,
QtCore.Qt.Horizontal,
)
):
item = DirectionGripItem(self, direction, i)
self.scene().addItem(item)
self.m_items.append(item)
def movePoint(self, i, p):
if 0 <= i < min(4, len(self.m_items)):
item_selected = self.m_items[i]
lp = self.mapFromScene(p)
self._radius = (lp - self.rect().center()).manhattanLength()
k = self.indexOf(lp)
if k is not None:
self.m_items = [item for item in self.m_items if not item.isSelected()]
self.m_items.insert(k, item_selected)
self.update_items_positions([k])
self.update_rect()
def update_items_positions(self, index_no_updates=None):
index_no_updates = index_no_updates or []
for i, (item, direction) in enumerate(
zip(
self.m_items,
(
QtCore.Qt.Vertical,
QtCore.Qt.Horizontal,
QtCore.Qt.Vertical,
QtCore.Qt.Horizontal,
),
),
):
item.m_index = i
if i not in index_no_updates:
pos = self.mapToScene(self.point(i))
item = self.m_items[i]
item._direction = direction
item.setEnabled(False)
item.setPos(pos)
item.setEnabled(True)
def indexOf(self, p):
for i in range(4):
if p == self.point(i):
return i
def point(self, index):
if 0 <= index < 4:
return [
QtCore.QPointF(0, -self.radius),
QtCore.QPointF(self.radius, 0),
QtCore.QPointF(0, self.radius),
QtCore.QPointF(-self.radius, 0),
][index]
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionHasChanged:
self.update_items_positions()
return
if change == QtWidgets.QGraphicsItem.ItemSceneHasChanged:
self.add_grip_items()
self.update_items_positions()
return
return super(CircleAnnotation, self).itemChange(change, value)
def hoverEnterEvent(self, event):
self.setBrush(QtGui.QColor(255, 0, 0, 100))
super(CircleAnnotation, self).hoverEnterEvent(event)
def hoverLeaveEvent(self, event):
self.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
super(CircleAnnotation, self).hoverLeaveEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
scene = QtWidgets.QGraphicsScene()
view = QtWidgets.QGraphicsView(scene)
view.setRenderHints(QtGui.QPainter.Antialiasing)
item = CircleAnnotation()
item.radius = 100
scene.addItem(item)
view.showMaximized()
sys.exit(app.exec_())
很抱歉重复询问有关 pyqt 的问题,但有限的资源迫使我这样做。
我一直在尝试为我的圆形实现一个调整大小的功能,这是 QGraphicsItem 的扩展,它有一个椭圆和一个居中的文本。我可以根据需要调整我的形状,但形状需要一段时间才能赶上鼠标,即在切换方向时,圆圈继续增加但需要一段时间才能切换方向,此外,圆圈的大小会随着顶部的锚点而调整边界矩形的左角。
请参阅
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):
self.prepareGeometryChange()
if self.handleSelected in [self.handleTopLeft,
self.handleTopRight,
self.handleBottomLeft,
self.handleBottomRight,
self.handleTopMiddle,
self.handleBottomMiddle,
self.handleMiddleLeft,
self.handleMiddleRight]:
self.radius += (mousePos.y() + mousePos.x() + self.mousePressPos.x() - self.mousePressPos.y())/64
self.setPos(self.x(),self.y())
self.update()
self.updateHandlesPos()
由于OP没有提供MRE,所以我从头开始创建了一个例子。其逻辑是跟踪item的变化,并据此计算出新的geometry,建立其他item的新位置。
from PyQt5 import QtWidgets, QtGui, QtCore
class GripItem(QtWidgets.QGraphicsPathItem):
circle = QtGui.QPainterPath()
circle.addEllipse(QtCore.QRectF(-5, -5, 10, 10))
square = QtGui.QPainterPath()
square.addRect(QtCore.QRectF(-10, -10, 20, 20))
def __init__(self, annotation_item, index):
super(GripItem, self).__init__()
self.m_annotation_item = annotation_item
self.m_index = index
self.setPath(GripItem.circle)
self.setBrush(QtGui.QColor("green"))
self.setPen(QtGui.QPen(QtGui.QColor("green"), 2))
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
self.setAcceptHoverEvents(True)
self.setZValue(11)
self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
def hoverEnterEvent(self, event):
self.setPath(GripItem.square)
self.setBrush(QtGui.QColor("red"))
super(GripItem, self).hoverEnterEvent(event)
def hoverLeaveEvent(self, event):
self.setPath(GripItem.circle)
self.setBrush(QtGui.QColor("green"))
super(GripItem, self).hoverLeaveEvent(event)
def mouseReleaseEvent(self, event):
self.setSelected(False)
super(GripItem, self).mouseReleaseEvent(event)
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
self.m_annotation_item.movePoint(self.m_index, value)
return super(GripItem, self).itemChange(change, value)
class DirectionGripItem(GripItem):
def __init__(self, annotation_item, direction=QtCore.Qt.Horizontal, parent=None):
super(DirectionGripItem, self).__init__(annotation_item, parent)
self._direction = direction
@property
def direction(self):
return self._direction
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
p = QtCore.QPointF(self.pos())
if self.direction == QtCore.Qt.Horizontal:
p.setX(value.x())
elif self.direction == QtCore.Qt.Vertical:
p.setY(value.y())
self.m_annotation_item.movePoint(self.m_index, p)
return p
return super(DirectionGripItem, self).itemChange(change, value)
class CircleAnnotation(QtWidgets.QGraphicsEllipseItem):
def __init__(self, radius=1, parent=None):
super(CircleAnnotation, self).__init__(parent)
self.setZValue(11)
self.m_items = []
self.setPen(QtGui.QPen(QtGui.QColor("green"), 4))
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
self.setAcceptHoverEvents(True)
self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
self._radius = radius
self.update_rect()
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, r):
if r <= 0:
raise ValueError("radius must be positive")
self._radius = r
self.update_rect()
self.add_grip_items()
self.update_items_positions()
def update_rect(self):
rect = QtCore.QRectF(0, 0, 2 * self.radius, 2 * self.radius)
rect.moveCenter(self.rect().center())
self.setRect(rect)
def add_grip_items(self):
if self.scene() and not self.m_items:
for i, (direction) in enumerate(
(
QtCore.Qt.Vertical,
QtCore.Qt.Horizontal,
QtCore.Qt.Vertical,
QtCore.Qt.Horizontal,
)
):
item = DirectionGripItem(self, direction, i)
self.scene().addItem(item)
self.m_items.append(item)
def movePoint(self, i, p):
if 0 <= i < min(4, len(self.m_items)):
item_selected = self.m_items[i]
lp = self.mapFromScene(p)
self._radius = (lp - self.rect().center()).manhattanLength()
k = self.indexOf(lp)
if k is not None:
self.m_items = [item for item in self.m_items if not item.isSelected()]
self.m_items.insert(k, item_selected)
self.update_items_positions([k])
self.update_rect()
def update_items_positions(self, index_no_updates=None):
index_no_updates = index_no_updates or []
for i, (item, direction) in enumerate(
zip(
self.m_items,
(
QtCore.Qt.Vertical,
QtCore.Qt.Horizontal,
QtCore.Qt.Vertical,
QtCore.Qt.Horizontal,
),
),
):
item.m_index = i
if i not in index_no_updates:
pos = self.mapToScene(self.point(i))
item = self.m_items[i]
item._direction = direction
item.setEnabled(False)
item.setPos(pos)
item.setEnabled(True)
def indexOf(self, p):
for i in range(4):
if p == self.point(i):
return i
def point(self, index):
if 0 <= index < 4:
return [
QtCore.QPointF(0, -self.radius),
QtCore.QPointF(self.radius, 0),
QtCore.QPointF(0, self.radius),
QtCore.QPointF(-self.radius, 0),
][index]
def itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionHasChanged:
self.update_items_positions()
return
if change == QtWidgets.QGraphicsItem.ItemSceneHasChanged:
self.add_grip_items()
self.update_items_positions()
return
return super(CircleAnnotation, self).itemChange(change, value)
def hoverEnterEvent(self, event):
self.setBrush(QtGui.QColor(255, 0, 0, 100))
super(CircleAnnotation, self).hoverEnterEvent(event)
def hoverLeaveEvent(self, event):
self.setBrush(QtGui.QBrush(QtCore.Qt.NoBrush))
super(CircleAnnotation, self).hoverLeaveEvent(event)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
scene = QtWidgets.QGraphicsScene()
view = QtWidgets.QGraphicsView(scene)
view.setRenderHints(QtGui.QPainter.Antialiasing)
item = CircleAnnotation()
item.radius = 100
scene.addItem(item)
view.showMaximized()
sys.exit(app.exec_())