def __activeControlMoved(self, pos): # The active control point has moved, update the control # rectangle control = self.__activeControl pos = control.pos() rect = QRectF(self.__rect) margins = self.__margins # TODO: keyboard modifiers and constraints. anchor = control.anchor() if anchor & ControlPoint.Top: rect.setTop(pos.y() + margins.top()) elif anchor & ControlPoint.Bottom: rect.setBottom(pos.y() - margins.bottom()) if anchor & ControlPoint.Left: rect.setLeft(pos.x() + margins.left()) elif anchor & ControlPoint.Right: rect.setRight(pos.x() - margins.right()) changed = self.__rect != rect self.blockSignals(True) self.setRect(rect) self.blockSignals(False) if changed: self.rectEdited.emit(rect.normalized())
class RectangleSelectionAction(UserInteraction): """ Select items in the scene using a Rectangle selection """ def __init__(self, document, *args, **kwargs): UserInteraction.__init__(self, document, *args, **kwargs) # The initial selection at drag start self.initial_selection = None # Selection when last updated in a mouseMoveEvent self.last_selection = None # A selection rect (`QRectF`) self.selection_rect = None # Keyboard modifiers self.modifiers = 0 def mousePressEvent(self, event): pos = event.scenePos() any_item = self.scene.item_at(pos) if not any_item and event.button() & Qt.LeftButton: self.modifiers = event.modifiers() self.selection_rect = QRectF(pos, QSizeF(0, 0)) self.rect_item = QGraphicsRectItem( self.selection_rect.normalized()) self.rect_item.setPen( QPen(QBrush(QColor(51, 153, 255, 192)), 0.4, Qt.SolidLine, Qt.RoundCap)) self.rect_item.setBrush(QBrush(QColor(168, 202, 236, 192))) self.rect_item.setZValue(-100) # Clear the focus if necessary. if not self.scene.stickyFocus(): self.scene.clearFocus() if not self.modifiers & Qt.ControlModifier: self.scene.clearSelection() event.accept() return True else: self.cancel(self.ErrorReason) return False def mouseMoveEvent(self, event): if not self.rect_item.scene(): # Add the rect item to the scene when the mouse moves. self.scene.addItem(self.rect_item) self.update_selection(event) return True def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: if self.initial_selection is None: # A single click. self.scene.clearSelection() else: self.update_selection(event) self.end() return True def update_selection(self, event): """ Update the selection rectangle from a QGraphicsSceneMouseEvent `event` instance. """ if self.initial_selection is None: self.initial_selection = set(self.scene.selectedItems()) self.last_selection = self.initial_selection pos = event.scenePos() self.selection_rect = QRectF(self.selection_rect.topLeft(), pos) # Make sure the rect_item does not cause the scene rect to grow. rect = self._bound_selection_rect(self.selection_rect.normalized()) # Need that 0.5 constant otherwise the sceneRect will still # grow (anti-aliasing correction by QGraphicsScene?) pw = self.rect_item.pen().width() + 0.5 self.rect_item.setRect(rect.adjusted(pw, pw, -pw, -pw)) selected = self.scene.items(self.selection_rect.normalized(), Qt.IntersectsItemShape, Qt.AscendingOrder) selected = set([item for item in selected if \ item.flags() & Qt.ItemIsSelectable]) if self.modifiers & Qt.ControlModifier: for item in selected | self.last_selection | \ self.initial_selection: item.setSelected((item in selected) ^ (item in self.initial_selection)) else: for item in selected.union(self.last_selection): item.setSelected(item in selected) self.last_selection = set(self.scene.selectedItems()) def end(self): self.initial_selection = None self.last_selection = None self.modifiers = 0 self.rect_item.hide() if self.rect_item.scene() is not None: self.scene.removeItem(self.rect_item) UserInteraction.end(self) def viewport_rect(self): """ Return the bounding rect of the document's viewport on the scene. """ view = self.document.view() vsize = view.viewport().size() viewportrect = QRect(0, 0, vsize.width(), vsize.height()) return view.mapToScene(viewportrect).boundingRect() def _bound_selection_rect(self, rect): """ Bound the selection `rect` to a sensible size. """ srect = self.scene.sceneRect() vrect = self.viewport_rect() maxrect = srect.united(vrect) return rect.intersected(maxrect)
class RectangleSelectionAction(UserInteraction): """ Select items in the scene using a Rectangle selection """ def __init__(self, document, *args, **kwargs): UserInteraction.__init__(self, document, *args, **kwargs) # The initial selection at drag start self.initial_selection = None # Selection when last updated in a mouseMoveEvent self.last_selection = None # A selection rect (`QRectF`) self.selection_rect = None # Keyboard modifiers self.modifiers = 0 def mousePressEvent(self, event): pos = event.scenePos() any_item = self.scene.item_at(pos) if not any_item and event.button() & Qt.LeftButton: self.modifiers = event.modifiers() self.selection_rect = QRectF(pos, QSizeF(0, 0)) self.rect_item = QGraphicsRectItem( self.selection_rect.normalized() ) self.rect_item.setPen( QPen(QBrush(QColor(51, 153, 255, 192)), 0.4, Qt.SolidLine, Qt.RoundCap) ) self.rect_item.setBrush( QBrush(QColor(168, 202, 236, 192)) ) self.rect_item.setZValue(-100) # Clear the focus if necessary. if not self.scene.stickyFocus(): self.scene.clearFocus() if not self.modifiers & Qt.ControlModifier: self.scene.clearSelection() event.accept() return True else: self.cancel(self.ErrorReason) return False def mouseMoveEvent(self, event): if not self.rect_item.scene(): # Add the rect item to the scene when the mouse moves. self.scene.addItem(self.rect_item) self.update_selection(event) return True def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: if self.initial_selection is None: # A single click. self.scene.clearSelection() else: self.update_selection(event) self.end() return True def update_selection(self, event): """ Update the selection rectangle from a QGraphicsSceneMouseEvent `event` instance. """ if self.initial_selection is None: self.initial_selection = set(self.scene.selectedItems()) self.last_selection = self.initial_selection pos = event.scenePos() self.selection_rect = QRectF(self.selection_rect.topLeft(), pos) # Make sure the rect_item does not cause the scene rect to grow. rect = self._bound_selection_rect(self.selection_rect.normalized()) # Need that 0.5 constant otherwise the sceneRect will still # grow (anti-aliasing correction by QGraphicsScene?) pw = self.rect_item.pen().width() + 0.5 self.rect_item.setRect(rect.adjusted(pw, pw, -pw, -pw)) selected = self.scene.items(self.selection_rect.normalized(), Qt.IntersectsItemShape, Qt.AscendingOrder) selected = set([item for item in selected if \ item.flags() & Qt.ItemIsSelectable]) if self.modifiers & Qt.ControlModifier: for item in selected | self.last_selection | \ self.initial_selection: item.setSelected( (item in selected) ^ (item in self.initial_selection) ) else: for item in selected.union(self.last_selection): item.setSelected(item in selected) self.last_selection = set(self.scene.selectedItems()) def end(self): self.initial_selection = None self.last_selection = None self.modifiers = 0 self.rect_item.hide() if self.rect_item.scene() is not None: self.scene.removeItem(self.rect_item) UserInteraction.end(self) def viewport_rect(self): """ Return the bounding rect of the document's viewport on the scene. """ view = self.document.view() vsize = view.viewport().size() viewportrect = QRect(0, 0, vsize.width(), vsize.height()) return view.mapToScene(viewportrect).boundingRect() def _bound_selection_rect(self, rect): """ Bound the selection `rect` to a sensible size. """ srect = self.scene.sceneRect() vrect = self.viewport_rect() maxrect = srect.united(vrect) return rect.intersected(maxrect)
class ControlPointRect(QGraphicsObject): Free = 0 KeepAspectRatio = 1 KeepCenter = 2 rectChanged = Signal(QRectF) rectEdited = Signal(QRectF) def __init__(self, parent=None, rect=None, constraints=0, **kwargs): QGraphicsObject.__init__(self, parent, **kwargs) self.setFlag(QGraphicsItem.ItemHasNoContents) self.setFlag(QGraphicsItem.ItemIsFocusable) self.__rect = rect if rect is not None else QRectF() self.__margins = QMargins() points = [ ControlPoint(self, ControlPoint.Left), ControlPoint(self, ControlPoint.Top), ControlPoint(self, ControlPoint.TopLeft), ControlPoint(self, ControlPoint.Right), ControlPoint(self, ControlPoint.TopRight), ControlPoint(self, ControlPoint.Bottom), ControlPoint(self, ControlPoint.BottomLeft), ControlPoint(self, ControlPoint.BottomRight), ] assert points == sorted(points, key=lambda p: p.anchor()) self.__points = dict((p.anchor(), p) for p in points) if self.scene(): self.__installFilter() for p in points: p.setFlag(QGraphicsItem.ItemIsFocusable) p.setFocusProxy(self) self.controlPoint(ControlPoint.Top).setConstraint(Qt.Vertical) self.controlPoint(ControlPoint.Bottom).setConstraint(Qt.Vertical) self.controlPoint(ControlPoint.Left).setConstraint(Qt.Horizontal) self.controlPoint(ControlPoint.Right).setConstraint(Qt.Horizontal) self.__constraints = constraints self.__activeControl = None self.__pointsLayout() def controlPoint(self, anchor): """ Return the anchor point (:class:`ControlPoint`) at anchor position or `None` if an anchor point is not set. """ return self.__points.get(anchor) def setRect(self, rect): """ Set the control point rectangle (:class:`QRectF`) """ if self.__rect != rect: self.__rect = QRectF(rect) self.__pointsLayout() self.prepareGeometryChange() self.rectChanged.emit(rect.normalized()) def rect(self): """ Return the control point rectangle. """ # Return the rect normalized. During the control point move the # rect can change to an invalid size, but the layout must still # know to which point does an unnormalized rect side belong, # so __rect is left unnormalized. # NOTE: This means all signal emits (rectChanged/Edited) must # also emit normalized rects return self.__rect.normalized() rect_ = Property(QRectF, fget=rect, fset=setRect, user=True) def setControlMargins(self, *margins): """Set the controls points on the margins around `rect` """ if len(margins) > 1: margins = QMargins(*margins) else: margins = margins[0] if isinstance(margins, int): margins = QMargins(margins, margins, margins, margins) if self.__margins != margins: self.__margins = margins self.__pointsLayout() def controlMargins(self): return self.__margins def setConstraints(self, constraints): raise NotImplementedError def isControlActive(self): """Return the state of the control. True if the control is active (user is dragging one of the points) False otherwise. """ return self.__activeControl is not None def itemChange(self, change, value): if change == QGraphicsItem.ItemSceneHasChanged and self.scene(): self.__installFilter() return QGraphicsObject.itemChange(self, change, value) def sceneEventFilter(self, obj, event): try: obj = toGraphicsObjectIfPossible(obj) if isinstance(obj, ControlPoint): etype = event.type() if (etype == QEvent.GraphicsSceneMousePress and event.button() == Qt.LeftButton): self.__setActiveControl(obj) elif (etype == QEvent.GraphicsSceneMouseRelease and event.button() == Qt.LeftButton): self.__setActiveControl(None) except Exception: log.error("Error in 'ControlPointRect.sceneEventFilter'", exc_info=True) return QGraphicsObject.sceneEventFilter(self, obj, event) def __installFilter(self): # Install filters on the control points. try: for p in self.__points.values(): p.installSceneEventFilter(self) except Exception: log.error("Error in ControlPointRect.__installFilter", exc_info=True) def __pointsLayout(self): """Layout the control points """ rect = self.__rect margins = self.__margins rect = rect.adjusted(-margins.left(), -margins.top(), margins.right(), margins.bottom()) center = rect.center() cx, cy = center.x(), center.y() left, top, right, bottom = rect.left(), rect.top(), rect.right( ), rect.bottom() self.controlPoint(ControlPoint.Left).setPos(left, cy) self.controlPoint(ControlPoint.Right).setPos(right, cy) self.controlPoint(ControlPoint.Top).setPos(cx, top) self.controlPoint(ControlPoint.Bottom).setPos(cx, bottom) self.controlPoint(ControlPoint.TopLeft).setPos(left, top) self.controlPoint(ControlPoint.TopRight).setPos(right, top) self.controlPoint(ControlPoint.BottomLeft).setPos(left, bottom) self.controlPoint(ControlPoint.BottomRight).setPos(right, bottom) def __setActiveControl(self, control): if self.__activeControl != control: if self.__activeControl is not None: self.__activeControl.positionChanged[QPointF].disconnect( self.__activeControlMoved) self.__activeControl = control if control is not None: control.positionChanged[QPointF].connect( self.__activeControlMoved) def __activeControlMoved(self, pos): # The active control point has moved, update the control # rectangle control = self.__activeControl pos = control.pos() rect = QRectF(self.__rect) margins = self.__margins # TODO: keyboard modifiers and constraints. anchor = control.anchor() if anchor & ControlPoint.Top: rect.setTop(pos.y() + margins.top()) elif anchor & ControlPoint.Bottom: rect.setBottom(pos.y() - margins.bottom()) if anchor & ControlPoint.Left: rect.setLeft(pos.x() + margins.left()) elif anchor & ControlPoint.Right: rect.setRight(pos.x() - margins.right()) changed = self.__rect != rect self.blockSignals(True) self.setRect(rect) self.blockSignals(False) if changed: self.rectEdited.emit(rect.normalized()) def boundingRect(self): return QRectF()
class ControlPointRect(QGraphicsObject): Free = 0 KeepAspectRatio = 1 KeepCenter = 2 rectChanged = Signal(QRectF) rectEdited = Signal(QRectF) def __init__(self, parent=None, rect=None, constraints=0, **kwargs): QGraphicsObject.__init__(self, parent, **kwargs) self.setFlag(QGraphicsItem.ItemHasNoContents) self.setFlag(QGraphicsItem.ItemIsFocusable) self.__rect = rect if rect is not None else QRectF() self.__margins = QMargins() points = \ [ControlPoint(self, ControlPoint.Left), ControlPoint(self, ControlPoint.Top), ControlPoint(self, ControlPoint.TopLeft), ControlPoint(self, ControlPoint.Right), ControlPoint(self, ControlPoint.TopRight), ControlPoint(self, ControlPoint.Bottom), ControlPoint(self, ControlPoint.BottomLeft), ControlPoint(self, ControlPoint.BottomRight) ] assert(points == sorted(points, key=lambda p: p.anchor())) self.__points = dict((p.anchor(), p) for p in points) if self.scene(): self.__installFilter() for p in points: p.setFlag(QGraphicsItem.ItemIsFocusable) p.setFocusProxy(self) self.controlPoint(ControlPoint.Top).setConstraint(Qt.Vertical) self.controlPoint(ControlPoint.Bottom).setConstraint(Qt.Vertical) self.controlPoint(ControlPoint.Left).setConstraint(Qt.Horizontal) self.controlPoint(ControlPoint.Right).setConstraint(Qt.Horizontal) self.__constraints = constraints self.__activeControl = None self.__pointsLayout() def controlPoint(self, anchor): """ Return the anchor point (:class:`ControlPoint`) at anchor position or `None` if an anchor point is not set. """ return self.__points.get(anchor) def setRect(self, rect): """ Set the control point rectangle (:class:`QRectF`) """ if self.__rect != rect: self.__rect = QRectF(rect) self.__pointsLayout() self.prepareGeometryChange() self.rectChanged.emit(rect.normalized()) def rect(self): """ Return the control point rectangle. """ # Return the rect normalized. During the control point move the # rect can change to an invalid size, but the layout must still # know to which point does an unnormalized rect side belong, # so __rect is left unnormalized. # NOTE: This means all signal emits (rectChanged/Edited) must # also emit normalized rects return self.__rect.normalized() rect_ = Property(QRectF, fget=rect, fset=setRect, user=True) def setControlMargins(self, *margins): """Set the controls points on the margins around `rect` """ if len(margins) > 1: margins = QMargins(*margins) else: margins = margins[0] if isinstance(margins, int): margins = QMargins(margins, margins, margins, margins) if self.__margins != margins: self.__margins = margins self.__pointsLayout() def controlMargins(self): return self.__margins def setConstraints(self, constraints): raise NotImplementedError def isControlActive(self): """Return the state of the control. True if the control is active (user is dragging one of the points) False otherwise. """ return self.__activeControl is not None def itemChange(self, change, value): if change == QGraphicsItem.ItemSceneHasChanged and self.scene(): self.__installFilter() return QGraphicsObject.itemChange(self, change, value) def sceneEventFilter(self, obj, event): try: obj = toGraphicsObjectIfPossible(obj) if isinstance(obj, ControlPoint): etype = event.type() if etype == QEvent.GraphicsSceneMousePress and \ event.button() == Qt.LeftButton: self.__setActiveControl(obj) elif etype == QEvent.GraphicsSceneMouseRelease and \ event.button() == Qt.LeftButton: self.__setActiveControl(None) except Exception: log.error("Error in 'ControlPointRect.sceneEventFilter'", exc_info=True) return QGraphicsObject.sceneEventFilter(self, obj, event) def __installFilter(self): # Install filters on the control points. try: for p in self.__points.values(): p.installSceneEventFilter(self) except Exception: log.error("Error in ControlPointRect.__installFilter", exc_info=True) def __pointsLayout(self): """Layout the control points """ rect = self.__rect margins = self.__margins rect = rect.adjusted(-margins.left(), -margins.top(), margins.right(), margins.bottom()) center = rect.center() cx, cy = center.x(), center.y() left, top, right, bottom = \ rect.left(), rect.top(), rect.right(), rect.bottom() self.controlPoint(ControlPoint.Left).setPos(left, cy) self.controlPoint(ControlPoint.Right).setPos(right, cy) self.controlPoint(ControlPoint.Top).setPos(cx, top) self.controlPoint(ControlPoint.Bottom).setPos(cx, bottom) self.controlPoint(ControlPoint.TopLeft).setPos(left, top) self.controlPoint(ControlPoint.TopRight).setPos(right, top) self.controlPoint(ControlPoint.BottomLeft).setPos(left, bottom) self.controlPoint(ControlPoint.BottomRight).setPos(right, bottom) def __setActiveControl(self, control): if self.__activeControl != control: if self.__activeControl is not None: self.__activeControl.positionChanged[QPointF].disconnect( self.__activeControlMoved ) self.__activeControl = control if control is not None: control.positionChanged[QPointF].connect( self.__activeControlMoved ) def __activeControlMoved(self, pos): # The active control point has moved, update the control # rectangle control = self.__activeControl pos = control.pos() rect = QRectF(self.__rect) margins = self.__margins # TODO: keyboard modifiers and constraints. anchor = control.anchor() if anchor & ControlPoint.Top: rect.setTop(pos.y() + margins.top()) elif anchor & ControlPoint.Bottom: rect.setBottom(pos.y() - margins.bottom()) if anchor & ControlPoint.Left: rect.setLeft(pos.x() + margins.left()) elif anchor & ControlPoint.Right: rect.setRight(pos.x() - margins.right()) changed = self.__rect != rect self.blockSignals(True) self.setRect(rect) self.blockSignals(False) if changed: self.rectEdited.emit(rect.normalized()) def boundingRect(self): return QRectF()
class ControlPointRect(QGraphicsObject): class Constraint(enum.IntEnum): Free = 0 KeepAspectRatio = 1 KeepCenter = 2 Free = Constraint.Free KeepAspectRatio = Constraint.KeepAspectRatio KeepCenter = Constraint.KeepCenter rectChanged = Signal(QRectF) rectEdited = Signal(QRectF) def __init__(self, parent=None, rect=QRectF(), constraints=Free, **kwargs): # type: (Optional[QGraphicsItem], QRectF, Constraint, Any) -> None super().__init__(parent, **kwargs) self.setFlag(QGraphicsItem.ItemHasNoContents) self.setFlag(QGraphicsItem.ItemIsFocusable) self.__rect = QRectF(rect) if rect is not None else QRectF() self.__margins = QMargins() points = [ ControlPoint(self, ControlPoint.Left, constraint=Qt.Horizontal, cursor=Qt.SizeHorCursor), ControlPoint(self, ControlPoint.Top, constraint=Qt.Vertical, cursor=Qt.SizeVerCursor), ControlPoint(self, ControlPoint.TopLeft, cursor=Qt.SizeFDiagCursor), ControlPoint(self, ControlPoint.Right, constraint=Qt.Horizontal, cursor=Qt.SizeHorCursor), ControlPoint(self, ControlPoint.TopRight, cursor=Qt.SizeBDiagCursor), ControlPoint(self, ControlPoint.Bottom, constraint=Qt.Vertical, cursor=Qt.SizeVerCursor), ControlPoint(self, ControlPoint.BottomLeft, cursor=Qt.SizeBDiagCursor), ControlPoint(self, ControlPoint.BottomRight, cursor=Qt.SizeFDiagCursor) ] assert (points == sorted(points, key=lambda p: p.anchor())) self.__points = dict((p.anchor(), p) for p in points) if self.scene(): self.__installFilter() for p in points: p.setFlag(QGraphicsItem.ItemIsFocusable) p.setFocusProxy(self) self.__constraints = constraints self.__activeControl = None # type: Optional[ControlPoint] self.__pointsLayout() def controlPoint(self, anchor): # type: (ControlPoint.Anchor) -> ControlPoint """ Return the anchor point (:class:`ControlPoint`) for anchor position. """ return self.__points[anchor] def setRect(self, rect): # type: (QRectF) -> None """ Set the control point rectangle (:class:`QRectF`) """ if self.__rect != rect: self.__rect = QRectF(rect) self.__pointsLayout() self.prepareGeometryChange() self.rectChanged.emit(rect.normalized()) def rect(self): # type: () -> QRectF """ Return the control point rectangle. """ # Return the rect normalized. During the control point move the # rect can change to an invalid size, but the layout must still # know to which point does an unnormalized rect side belong, # so __rect is left unnormalized. # NOTE: This means all signal emits (rectChanged/Edited) must # also emit normalized rects return self.__rect.normalized() rect_ = Property(QRectF, fget=rect, fset=setRect, user=True) def setControlMargins(self, *margins): # type: (int) -> None """Set the controls points on the margins around `rect` """ if len(margins) > 1: margins = QMargins(*margins) elif len(margins) == 1: margin = margins[0] margins = QMargins(margin, margin, margin, margin) else: raise TypeError if self.__margins != margins: self.__margins = margins self.__pointsLayout() def controlMargins(self): # type: () -> QMargins return QMargins(self.__margins) def setConstraints(self, constraints): raise NotImplementedError def isControlActive(self): # type: () -> bool """Return the state of the control. True if the control is active (user is dragging one of the points) False otherwise. """ return self.__activeControl is not None def itemChange(self, change, value): # type: (QGraphicsItem.GraphicsItemChange, Any) -> Any if change == QGraphicsItem.ItemSceneHasChanged and self.scene(): self.__installFilter() return super().itemChange(change, value) def sceneEventFilter(self, obj, event): # type: (QGraphicsItem, QEvent) -> bool obj = toGraphicsObjectIfPossible(obj) if isinstance(obj, ControlPoint): etype = event.type() if etype in (QEvent.GraphicsSceneMousePress, QEvent.GraphicsSceneMouseDoubleClick) and \ event.button() == Qt.LeftButton: self.__setActiveControl(obj) elif etype == QEvent.GraphicsSceneMouseRelease and \ event.button() == Qt.LeftButton: self.__setActiveControl(None) return super().sceneEventFilter(obj, event) def __installFilter(self): # type: () -> None # Install filters on the control points. for p in self.__points.values(): p.installSceneEventFilter(self) def __pointsLayout(self): # type: () -> None """Layout the control points """ rect = self.__rect margins = self.__margins rect = rect.adjusted(-margins.left(), -margins.top(), margins.right(), margins.bottom()) center = rect.center() cx, cy = center.x(), center.y() left, top, right, bottom = \ rect.left(), rect.top(), rect.right(), rect.bottom() self.controlPoint(ControlPoint.Left).setPos(left, cy) self.controlPoint(ControlPoint.Right).setPos(right, cy) self.controlPoint(ControlPoint.Top).setPos(cx, top) self.controlPoint(ControlPoint.Bottom).setPos(cx, bottom) self.controlPoint(ControlPoint.TopLeft).setPos(left, top) self.controlPoint(ControlPoint.TopRight).setPos(right, top) self.controlPoint(ControlPoint.BottomLeft).setPos(left, bottom) self.controlPoint(ControlPoint.BottomRight).setPos(right, bottom) def __setActiveControl(self, control): # type: (Optional[ControlPoint]) -> None if self.__activeControl != control: if self.__activeControl is not None: self.__activeControl.positionChanged[QPointF].disconnect( self.__activeControlMoved) self.__activeControl = control if control is not None: control.positionChanged[QPointF].connect( self.__activeControlMoved) def __activeControlMoved(self, pos): # type: (QPointF) -> None # The active control point has moved, update the control # rectangle control = self.__activeControl assert control is not None pos = control.pos() rect = QRectF(self.__rect) margins = self.__margins # TODO: keyboard modifiers and constraints. anchor = control.anchor() if anchor & ControlPoint.Top: rect.setTop(pos.y() + margins.top()) elif anchor & ControlPoint.Bottom: rect.setBottom(pos.y() - margins.bottom()) if anchor & ControlPoint.Left: rect.setLeft(pos.x() + margins.left()) elif anchor & ControlPoint.Right: rect.setRight(pos.x() - margins.right()) changed = self.__rect != rect self.blockSignals(True) self.setRect(rect) self.blockSignals(False) if changed: self.rectEdited.emit(rect.normalized()) def boundingRect(self): # type: () -> QRectF return QRectF()