Issue
I am sorry for repetitively asking questions regarding pyqt, but the limited resources on it force me to.
I have been trying to implement a resize function for my circle shape, that is an extension of QGraphicsItem, which has an ellipse and a centered text. I am able to resize my shape as desired, but the shape takes a while to catch up to the mouse, i.e on switching directions the circle continues to increase but takes a while to switch directions, moreover the circle resizes with an anchor at the top left corner of the bounding rect.
See this question for a bit of code background
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()
Solution
Since the OP has not provided an MRE then I have created an example from scratch. The logic is to track the changes of the items and according to that calculate the new geometry and establish the new position of the other items.
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_())
Answered By - eyllanesc
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.