Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Unsolved 5 days. I have been trying to center a freakin QGraphicsItem hierarchy. They made this hard.

    General and Desktop
    3
    11
    1551
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • enjoysmath
      enjoysmath last edited by enjoysmath

      I have tried:

      • setTransformationOriginPoint
      • Translating the child item by boundingRect().center()
      • setTransform()
      • A million other things (5 days!!)

      I have a child QGraphicsObject (a control point) part of a GraphicsSet item, which is a child of a Line item.

      The best I've gotten it is this: Every time I press an item with the mouse, it's lower/upper/left/right corner (depends on control point positions) becomes center or in other words that corner shifts to the mouse's position. Which is what i want, except I want the mouse cursor to end up on the item's boundingRect's center, not a corner!

      Don't give me this, moveBy, setPos, setTransform crap. It does not work!

      from PyQt5.QtWidgets import (QApplication, QMainWindow, QGraphicsScene, QGraphicsView, 
                                   QGraphicsObject, QGraphicsSceneEvent)
      from PyQt5.QtGui import QBrush, QPen, QColor, QPainterPathStroker
      from PyQt5.QtCore import QPointF, Qt, QRectF, pyqtSignal, QEvent
      import sys
      
      from PyQt5.QtCore import QObject, pyqtBoundSignal, QLineF
      import inspect
      
      
      class PickleableSignal(QObject):
          """
          Wrapper class for a single signal, returned by __getattribute__ method of SignalHelper.
          Supports, lambda, delegates, disconnection by string or by another reference / copy of the
          lambda or delegate.  Supports pickling of everything using the dill library.
          For pickling to work, sender must be pickleable on its own in the usual way.
          """
          
          Handle, Slot = range(2)
          
          def __init__(self, sender=None, signal=None, init=None):
              assert(isinstance(sender, SignalPickler))    # Requires inheritance to work
              super().__init__()
              if init in [None, True]:
                  self._sender = sender
                  self._signal = signal
                  self._slotInfo = {}       # Keyed by string representing lambda or delegate name
          
          def __setstate__(self, data):
              """
              Standard pickle method design.  Includes slot info and connection state.
              """
              self.__init__(init=False)
              self._sender = data['sender']
              self._signal = data['signal']
              self._slotInfo = {}
              # PyQt5 is not pickle aware so we must reconnect the signals after unpickling and/or copying.
              for slot_id, (handle, slot) in data['slot info'].items():
                  self.connect(slot)
                  
          def __getstate__(self):
              """
              Standard pickle method design.  Includes slot info.
              """
              return {
                  'sender' : self._sender,
                  'signal' : self._signal,
                  'slot info' : self._slotInfo,
              }
          
          def __deepcopy__(self, memo):
              """
              Standard deepcopy method design.  Includes slot info and connection state.
              """
              from copy import deepcopy
              copy = type(self)(init=False)
              memo[id(self)] = copy
              copy._sender = deepcopy(self._sender, memo)
              copy._signal = self._signal    # OK, since we treat _signal as an immutable string.
              copy._slotInfo = {}
              # Of course we need to create this copies own slot connections.
              for slot_id, (handle, slot) in self._slotInfo.items():
                  copy.connect(slot)  
              return copy
          
          def connect(self, slot):
              """
              Allows a given slot signature to be connected once and only once.
              Returns the slot id to use for disconnecting; alternatively pass in the exact
              same lambda or delegate and that should do the trick.  Returns None if 
              that same lambda or delegate has already been added.
              """
              if not self._sender.isSignalRegistered(self):
                  self._sender.registerSignal(self)
              slot_id = self.slotID(slot)
              if slot_id not in self._slotInfo:
                  self._slotInfo[slot_id] = (self._sender.pyqtSignal(self._signal).connect(slot), slot)
                  return slot_id
              return None
          
          def emit(self, *args):
              self._sender.pyqtSignal(self._signal).emit(*args)
              
          def disconnect(self, slot):
              """
              Pass in a string for the delegate / lambda or the delegate lambda itself and we'll
              generate the canonical id ourself.
              """
              if not isinstance(slot, str):
                  slot = self.slotID(slot)
              handle = self._slotInfo[slot][self.Handle]
              self._sender.pyqtSignal(self._signal).disconnect(handle)
              del self._slotInfo[slot]   # Don't forget to delete its entry
          
          def signalName(self):
              return self._signal
          
          def senderObj(self):
              return self._sender
          
          def slot(self, slot_id):
              return self._slotInfo[slot_id][self.Slot]
                  
          def slotID(self, slot):
              """
              This is how we generated an identifier string given a slot.
              """
              return inspect.getsource(slot).strip()
          
          def __contains__(self, slot):
              if not isinstance(slot, str):
                  slot = self.slotID(slot)
              return slot in self._slotInfo
          
          
      class SignalPickler:
          """
          A mixin class that helps connect / disconnect slots to member signals and pickle them (using dill).
          It should go "GraphicsBase(QGraphicsObject, SignalPickler)" as far as base class ordering goes.
          I've never found the other way around to work without compiler error or unexplicable runtime crash.
          The same goes for other classes.  Qt's raw class first, then your mixin or QObject-derived class.
          """    
          
          def __init__(self):
              self._signals = {}
              
          def __setstate__(self, data):
              """
              Subclasses must call this class's __setstate__ in order to pickle signals.
              """
              self.__init__()
              self._signals = data['signals']
              
          def __getstate__(self):
              """
              Subclasses must also call this class's __getstate__ in order to pickle signals.
              """
              return {
                  'signals' : self._signals
              }
          
          def __deepcopy__(self, memo):
              """
              Want your objects / widgets copy & pasteable?  Then also call this from a subclass's __deepcopy__ method.
              """
              copy = type(self)()
              memo[id(self)] = copy
              copy._signals = deepcopy(self._signals, memo)
              return copy
          
          def __getattribute__(self, attr):
              """
              Internally, this is what SignalPickler does.  Every time you ask for a signal from the sender object,
              for example: `sender.positionChanged.connect(foo)`, instead of returning sender.positionChanged it will
              return a SignalPickler object wrapping it.
              """
              res = super().__getattribute__(attr)
              if isinstance(res, pyqtBoundSignal):
                  return PickleableSignal(sender=self, signal=attr)
              return res    
          
          def registerSignal(self, signal):
              """
              For internal use only, usually.
              """
              self._signals[signal.signalName()] = signal
              
          def isSignalRegistered(self, signal):
              """
              For internal use only, usually.
              """
              return signal.signalName() in self._signals
          
          def pyqtSignal(self, name):
              return super().__getattribute__(name)
          
          
      class Graphics(QGraphicsObject, SignalPickler):
          anythingHasChanged = pyqtSignal(str)
          changeToAnything = pyqtSignal(str)    
          positionChange = pyqtSignal(QPointF)
          positionHasChanged = pyqtSignal(QPointF)
          parentChange = pyqtSignal(QObject)
          parentHasChanged = pyqtSignal(QObject)
          childAdded = pyqtSignal(QObject)
          childRemoved = pyqtSignal(QObject)
          brushChange = pyqtSignal(QBrush)
          brushHasChanged = pyqtSignal(QBrush)
          penChange = pyqtSignal(QPen)
          penHasChanged = pyqtSignal(QPen)
              
          def __init__(self, pen=None, brush=None, children=None, parent=None, init=None):
              if init in [None, True]:
                  super().__init__()
                  super().__init__()
                  if pen is None:
                      pen = QPen(QColor(255, 0, 0), 1.0)
                  if brush is None:
                      brush = QBrush(QColor(255, 0, 0))
                  self._brush = brush
                  self._pen = pen     
                  self.setParentItem(parent)
                  self.setFlags(self.ItemIsFocusable | self.ItemIsMovable |
                                self.ItemIsSelectable | self.ItemSendsGeometryChanges |
                                self.ItemSendsScenePositionChanges)
              self._surpressSigs = False
              self._prevBrush = self._brush
              self._prevPen = self._pen
              self._prevParent = parent
              self._prevPos = self.pos()   
              if children is None:
                  children = []
              for child in children:
                  if child: child.setParentItem(self)
      
          def boundingRect(self):
              return self.childrenBoundingRect()
      
          def itemChange(self, change, value):
              if change == self.ItemParentChange:
                  self._prevParent = self.parentItem()
                  self.emitChange(self.parentChange, 'parentItem', value)
              elif change == self.ItemParentHasChanged:
                  self.emitHasChanged(self.parentHasChanged, 'parentItem', value)
              elif change == self.ItemPositionChange:
                  self._prevPos = value
                  self.emitChange(self.positionChange, 'pos', value)
              elif change == self.ItemPositionHasChanged:
                  self.emitHasChanged(self.positionHasChanged, 'pos', value)        
              elif change == self.ItemChildAddedChange:
                  self.emitHasChanged(self.childAdded, 'childItems', value)
              elif change == self.ItemChildRemovedChange:
                  self.emitHasChanged(self.childRemoved, 'childItems', value)
              elif change == self.ItemSceneChange:
                  if self.scene():
                      self.removedFromSceneEvent()
              elif change == self.ItemSceneHasChanged:
                  if self.scene():
                      self.addedToSceneEvent()
              return super().itemChange(change, value)
          
          def setBrush(self, QBrush):
              self.emitChange(self.brushChange, 'QBrush', QBrush)
              self._prevBrush = self._brush
              self._brush = QBrush
              self.update()
              self.emitHasChanged(self.brushHasChanged, 'QBrush', QBrush)
              
          def QBrush(self):
              return self._brush
          
          def setPen(self, pen):
              self.emitChange(self.penChange, 'pen', pen)
              self._prevPen = self._pen
              self._pen = pen
              self.update()
              self.emitHasChanged(self.penHasChanged, 'pen', pen)
              
          def pen(self):
              return self._pen
          
          def setSignalsSurpressed(self, en):
              if self._surpressSigs != en:
                  self._surpressSigs = en
                  for child in self.childItems():
                      child.setSignalsSurpressed(en)
                  
          def signalsSurpressed(self):
              return self._surpressSigs
          
          def emitChange(self, sig, member, *args):
              if not self._surpressSigs:
                  sig.emit(*args)
                  self.changeToAnything.emit(member)
              
          def emitHasChanged(self, sig, member, *args):
              if not self._surpressSigs:
                  sig.emit(*args)
                  self.anythingHasChanged.emit(member)        
                  
          def paint(self, painter, option, widget):
              from geom_tools import paintSelectionShape
              if self.isSelected():
                  paintSelectionShape(painter, self)
              painter.setRenderHints(painter.HighQualityAntialiasing | painter.Antialiasing)
              
          def childPosHasChangedEvent(self, child):
              pass
          
          def childPosChangeEvent(self, child):
              pass
      
          def addedToSceneEvent(self):
              """
              Do here init things that can only be done once added to the scene.
              """        
              parent = self.parentItem()
              if isinstance(parent, Graphics):
                  self.installEventFilter(parent)
              for child in self.childItems():
                  child.addedToSceneEvent()
                      
          def removedFromSceneEvent(self):
              """
              Undo things done in the above method.
              """
              parent = self.parentItem()
              if isinstance(parent, Graphics):
                  self.removeEventFilter(parent)
              for child in self.childItems():
                  child.removedFromSceneEvent()
                  
          def mousePressEvent(self, event):
              if event.button() == Qt.LeftButton:
                  self.setPos(self.mapToParent(event.pos()))
              super().mousePressEvent(event)
      
      
      from collections import OrderedDict
      
      class GraphicsSet(Graphics):
          """
          Stores graphics items naturally by id() but they're also part of the same
          movable group.  They are also kept ordered (order of element addition).
          """
          
          elementPosHasChanged = pyqtSignal(Graphics)
          elementPosChange = pyqtSignal(Graphics)
          
          def __init__(self, count=None, type=None, parent=None, init=None, *args, **kwargs):
              if init in [None, True]:
                  super().__init__(parent=parent)
                  self._set = OrderedDict()
                  if count is not None and type is not None:
                      for k in range(0, count):
                          obj = type(*args, **kwargs)
                          self.add(obj)
              
          def boundingRect(self):
              return self.childrenBoundingRect()    
          
          def add(self, obj):
              self._set[id(obj)] = obj
              obj.setParentItem(self)
              
          def remove(self, obj):
              del self._set[id(obj)]
              obj.setParentItem(None)
              
          def elements(self):
              return self._set.values()
          
          def odict(self):
              return self._set
          
          def __iter__(self):
              return iter(self._set)
          
          def __len__(self):
              return len(self._set)    
                  
          #def paint(self, painter, option, widget):
              #from PyQt5.QtGui import QColor, QPen
              #painter.setPen(QPen(QColor(0, 0, 255), 2.0))
              #painter.drawPoint(self.pos())
              #super().paint(painter, option, widget)
          
          def childPosChangeEvent(self, child):
              self.elementPosChange.emit(child)    
          
          def childPosHasChangedEvent(self, child):
              self.elementPosHasChanged.emit(child) 
              
              
      class Rectangle(Graphics):
          rectangleChange = pyqtSignal(Graphics, QRectF)
          rectangleHasChanged = pyqtSignal(Graphics, QRectF)
          
          def __init__(self, x=None, y=None, w=None, h=None, pen=None, QBrush=None, 
                       children=None, parent=None, init=None):
              if init in [None, True]:
                  super().__init__(pen, QBrush, children, parent)
                  if x is None:
                      x = 0
                  if y is None:
                      y = 0
                  if w is None:
                      w = 10
                  if h is None:
                      h = 5
                  self.setPos(QPointF(x, y))
                  rect = QRectF(x, y, w, h)
                  self._rect = rect.translated(-w/2, -h/2)
                      
          def __setstate__(self, data):
              super().__setstate__(data)
              self.setRect(data['rect'])
              self.__init__(init=False)
              
          def __getstate__(self):
              data = super().__getstate__()
              data['rect'] = self.rect()
              return data
          
          def __deepcopy__(self, memo):
              copy = super().__deepcopy__(memo)
              copy.setRect(self.rect())
              return copy
          
          def boundingRect(self):
              w = self.pen().widthF() / 2
              return self.rect().adjusted(-w, -w, w, w)
          
          def shape(self):
              path = QPainterPath()
              w = self.pen().widthF() / 2
              path.addRect(self.rect().adjusted(-w, -w, w, w))
              return path
          
          def paint(self, painter, option, widget):
              super().paint(painter, option, widget)
              painter.setPen(self.pen())
              painter.setBrush(self.QBrush())
              painter.drawRect(self.rect())
              
          def rect(self):
              return self._rect
          
          def setRect(self, rect):
              rect = rect.translated(-rect.width()/2, -rect.height()/2)
              if self._rect != rect:
                  self.emitChange(self.rectangleChange, 'rect', rect)
                  self._rect = rect
                  self.update()
                  self.emitHasChanged(self.rectangleHasChanged, 'rect', rect)
                          
                          
      from PyQt5.QtGui import QPainterPath
      
      class Ellipse(Rectangle):
          def __init__(self, x=None, y=None, w=None, h=None, pen=None, QBrush=None,
                       children=None, parent=None, init=None):
              if init in [None, True]:
                  super().__init__(x, y, w, h, pen, QBrush)
          
          def shape(self):
              path = QPainterPath()
              w = self.pen().widthF() / 2
              path.addEllipse(self.rect().adjusted(-w, -w, w, w))
              return path
          
          def paint(self, painter, option, widget):
              Graphics.paint(self, painter, option, widget)
              painter.setPen(self.pen())
              painter.setBrush(self.QBrush())
              painter.drawEllipse(self.rect())    
              
              
      class Point(Ellipse):
          def __init__(self, x=None, y=None, diam=None, pen=None, brush=None,
                       children=None, parent=None, init=None):
              if init in [None, True]:
                  return super().__init__(x, y, diam, diam, pen, brush, children, parent)
              
      
      class Line(Graphics):
          def __init__(self, points=None, diam=None, pen=None, 
                       point_pen=None, point_brush=None, parent=None, init=None):
              if points is None:
                  points = GraphicsSet(count=2, type=Point, diam=diam, pen=point_pen, brush=point_brush)
              if init in [None, True]:
                  super().__init__(pen, children=[points], parent=parent)
                  self.controlPoints = points
              self.controlPoints.elementPosHasChanged.connect(self.controlPointPosChangedEvent)
              self.setFiltersChildEvents(True)
              
          def __setstate__(self, data):
              super().__setstate__(data)
              self._points = data['points']
              self.__init__()
              
          def __getstate__(self):
              return {
                  'parent' : self.parentItem(),
                  'pos' : self.pos(),
                  'control points' : self.controlPoints,
              }
          
          def __deepcopy__(self, memo):
              copy = deepcopy(super(), memo)
              copy.controlPoints = deepcopy(self.controlPoints, memo)
              return copy
          
          def line(self):
              points = self.pointPos()
              return QLineF(points[0], points[-1])        
      
          def shape(self):
              path = QPainterPath()
              line = self.line()
              path.moveTo(line.p1())
              path.lineTo(line.p2())
              stroker = QPainterPathStroker(QPen(Qt.black, self.pen().widthF())) 
              return stroker.createStroke(path)     
          
          def paint(self, painter, option, widget):
              super().paint(painter, option, widget)
              if __debug__:
                  painter.setPen(self.pen())
                  painter.drawRect(self.boundingRect())
              painter.setPen(self.pen())
              painter.setBrush(self.QBrush())
              painter.drawLine(self.line())
      
          def controlPointPosChangedEvent(self, point):
              #"""
              #Ensures that self.line().center() == self.pos() always.
              #"""
              #child = self.controlPoints
              ##self.controlPoints.setPos(self.controlPoints.pos() -
              self.setSignalsSurpressed(True)
              #parent = self.parentItem()
              transform = QTransform()
              transform.translate(-self.boundingRect().center().x(), -self.boundingRect().center().y())
              self.setTransform(transform)
              self.setSignalsSurpressed(False)      
              
              ## When a control point position has changed, we should repaint the line:
              self.updateScene()
              
          def pointPos(self):
              return [self.controlPoints.mapToParent(elem.pos()) 
                      for elem in self.controlPoints.elements()]
          
          def p1(self):
              return self.pointPos()[0]
          
          def p2(self):
              return self.pointPos()[-1]
          
          def setP1(self, p):
              p = self.controlPoints.mapFromParent(p)
              if self.p1() != p:
                  self.controlPoints.elements()[0].setPos(p)
          
          def setP2(self, p):
              p = self.controlPoints.mapFromParent(p)
              if self.p2() != p:
                  self.controlPoints.elements()[-1].setPos(p)
              
          #def sceneEventFilter(self, watched, event):
              #if watched is self.controlPoints:
                  #if event.type() == QEvent.GraphicsSceneMouseMove:
                      #if self.scene():
                          #item = self.scene().itemAt(event.scenePos(), QTransform())
                          #if item and item in self.controlPoints:
                              #return True
                      #self.setSignalsSurpressed(True)
                      #delta = event.pos()
                      #delta = self.mapToParent(delta)
                      #self.setPos(delta)
                      #self.controlPoints.setPos(-self.boundingRect().center())
                      #self.setSignalsSurpressed(False)
                      #return True
              #return False    
              
          def mousePressEvent(self, event):
              super().mousePressEvent(event)
              self.setSignalsSurpressed(True)
              #self.setPos(event.pos())
              #self.controlPoints.setPos(-self.boundingRect().center())
              rect = self.controlPoints.boundingRect()
              trans = QTransform()
              trans.translate(-self.boundingRect().center().x(), -self.boundingRect().center().y())
              self.controlPoints.setTransform(trans)
              self.setSignalsSurpressed(False)
      
              
      if __name__ == '__main__':
          app = QApplication([])
          
          window = QMainWindow()
          window.view = QGraphicsView()
          window.scene = QGraphicsScene()
          window.view.setScene(window.scene)
          window.show()
          window.setCentralWidget(window.view)
          
          item = Line()
          window.scene.addItem(item)
          
          sys.exit(app.exec_())
      
      

      https://github.com/enjoysmath
      https://math.stackexchange.com/users/26327/exercisingmathematician

      raven-worx 1 Reply Last reply Reply Quote 0
      • raven-worx
        raven-worx Moderators @enjoysmath last edited by

        @enjoysmath said in 5 days. I have been trying to center a freakin QGraphicsItem hierarchy. They made this hard.:

        Don't give me this, moveBy, setPos, setTransform crap. It does not work!

        when it doesn't work, you are doing it wrong, obviously.
        Easiest would be when you show some code.

        --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
        If you have a question please use the forum so others can benefit from the solution in the future

        enjoysmath 1 Reply Last reply Reply Quote 0
        • enjoysmath
          enjoysmath last edited by

          I posted the code. I put it all in one file since I couldn't upload a document zip. Run it, or let me know if it doesn't immediately run. Firs spread the control points in the view. Then try moving the rectangle itself by selecting any part of the rectangle that's not at a control point. The lower / upper / left or right corner gets sucked into the mouse cursor position. I'd like the same to happen but with the center of the boundingRect(), not the corner!

          I have tried at least 100 "permutations" on how it would work from my perfect understanding of the coordinate transformation system. Much of them failed to have an effect on the corner thing. And when they really suck, the whole scene just blows up or the item jets off or starts shaking abrubtly. Why is it so hard to do this? That makes no sense to me.

          https://github.com/enjoysmath
          https://math.stackexchange.com/users/26327/exercisingmathematician

          1 Reply Last reply Reply Quote 0
          • enjoysmath
            enjoysmath @raven-worx last edited by

            @raven-worx

            How is the code above?

            https://github.com/enjoysmath
            https://math.stackexchange.com/users/26327/exercisingmathematician

            raven-worx 1 Reply Last reply Reply Quote 0
            • raven-worx
              raven-worx Moderators @enjoysmath last edited by

              @enjoysmath
              sry i am not very good with Python. Maybe someone else can help here?
              When i spot something i let you know.

              --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
              If you have a question please use the forum so others can benefit from the solution in the future

              enjoysmath 2 Replies Last reply Reply Quote 0
              • enjoysmath
                enjoysmath @raven-worx last edited by

                @raven-worx Thanks. Anything you code in C++ I can convert to Python easily. I'm familiar with Python mostly, but have toiled in C++ / QtCreator.

                This problem is not specific to Python, I am 100% confident that you will find the same graphics item "features" in an analogous C++ version.

                https://github.com/enjoysmath
                https://math.stackexchange.com/users/26327/exercisingmathematician

                1 Reply Last reply Reply Quote 0
                • enjoysmath
                  enjoysmath @raven-worx last edited by enjoysmath

                  @raven-worx In other words, given a graphics item hierarchy : A -> B -> C, where -> is "is a parent of", how would you let C move freely (control points), where B's boundingRect() is the minimal bounding rect of all control points C in B, and I want to keep B's boundingRect() appearing centered on A's position?

                  Hold on... that just gave me an idea.

                  K, stuck in same boat. Here is a Pain3D png of what I want for the control point drag action.
                  0_1555715275862_PyQt5cumber_ItemCenteringBug1.png

                  By "origin" in the image I mean pos()

                  https://github.com/enjoysmath
                  https://math.stackexchange.com/users/26327/exercisingmathematician

                  KillerSmath 1 Reply Last reply Reply Quote 0
                  • KillerSmath
                    KillerSmath @enjoysmath last edited by

                    @enjoysmath

                    The (0,0) point inside a child is the same (0,0) point inside your parent.

                    Noticed, when you are trying to centralize an object in a graphicsview, you have to translate a half to left and top to keep it on center:
                    Rect(-5,-5,10,10) => Centralized Rect with Width and Height = 10 on position (0,0).

                    By what i understand, you are trying to resize the parent by child position B -> C. I had a similiar problem in an old project. See an example.

                    0_1555716971827_Peek 19-04-2019 19-30.gif

                    I am not sure how you can achieve this goal on PyQt but i can give you an example on Cpp.

                    .h file

                    class MovableCircle : public QGraphicsObject
                    {
                        Q_OBJECT
                    public:
                        explicit MovableCircle(QGraphicsItem *parent = 0);
                    
                    private:
                        QRectF boundingRect() const;
                        QPainterPath shape() const;
                        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
                        void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
                        void mousePressEvent(QGraphicsSceneMouseEvent *event);
                        void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
                        QPointF _shiftMouseCoords;
                    
                    signals:
                        void circleMoved();
                    };
                    
                    class BoundedRect : public QGraphicsObject
                    {
                        Q_OBJECT
                    public:
                        BoundedRect(QGraphicsItem *parent = 0);
                        QRectF boundingRect() const;
                    
                    private:
                        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
                        MovableCircle *_topLeftCircle, *_topRightCircle, *_bottomLeftCircle, *_bottomRightCircle;
                        QSizeF _size;
                    };
                    

                    .cpp file

                    #include "moveitem.h"
                    
                    #include <QtMath>
                    
                    //////////////////////
                    ///  Movable Circle
                    //////////////////////
                    
                    MovableCircle::MovableCircle(QGraphicsItem *parent) :
                        QGraphicsObject(parent)
                    {
                        setFlag(ItemClipsToShape, true);
                        setCursor(QCursor(Qt::PointingHandCursor));
                    }
                    
                    QRectF MovableCircle::boundingRect() const
                    {
                        qreal adjust = 0.5;
                        return QRectF(-5 - adjust, -5 - adjust,
                                      10 + adjust, 10 + adjust);
                    }
                    
                    QPainterPath MovableCircle::shape() const
                    {
                        QPainterPath path;
                        qreal adjust = 0.5;
                        path.addEllipse(-5 - adjust, -5 - adjust,
                                        10 + adjust, 10 + adjust);
                        return path;
                    }
                    
                    void MovableCircle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
                    {
                        Q_UNUSED(option);
                        Q_UNUSED(widget);
                    
                        painter->setBrush(QBrush(Qt::red));
                        painter->setPen(QPen(Qt::black));
                        painter->drawEllipse(-5, -5, 10, 10);
                    }
                    
                    void MovableCircle::mousePressEvent(QGraphicsSceneMouseEvent *event)
                    {
                        _shiftMouseCoords = this->pos() - mapToScene(event->pos());
                        this->setCursor(QCursor(Qt::ClosedHandCursor));
                    }
                    
                    void MovableCircle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
                    {
                        setPos(mapToScene(event->pos() + _shiftMouseCoords));
                        emit circleMoved();
                    }
                    
                    void MovableCircle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
                    {
                        Q_UNUSED(event);
                        this->setCursor(QCursor(Qt::PointingHandCursor));
                    }
                    
                    //////////////////////
                    ///  Bounded Class
                    //////////////////////
                    
                    BoundedRect::BoundedRect(QGraphicsItem *parent)
                        : QGraphicsObject(parent), _size(180, 180) // Using the Construtor to setup te variable
                    {
                        setFlag(QGraphicsItem::ItemIsMovable, true);
                    
                        // Top Left
                        _topLeftCircle = new MovableCircle(this);
                        _topLeftCircle->setPos(-_size.width()/2, -_size.width()/2);
                        // Top Right
                        _topRightCircle = new MovableCircle(this);
                        _topRightCircle->setPos(_size.width()/2, -_size.width()/2);
                        // Bottom Left
                        _bottomLeftCircle = new MovableCircle(this);
                        _bottomLeftCircle->setPos(-_size.width()/2, _size.width()/2);
                        // Bottom Right
                        _bottomRightCircle = new MovableCircle(this);
                        _bottomRightCircle->setPos(_size.width()/2, _size.width()/2);
                    
                        // Signals
                        // If a delimiter point has been moved, so force the item to redraw
                        
                        connect(_topLeftCircle, &MovableCircle::circleMoved, this, [this](){
                            _bottomLeftCircle->setPos(_topLeftCircle->pos().x(), _bottomLeftCircle->pos().y());
                            _topRightCircle->setPos(_topRightCircle->pos().x(), _topLeftCircle->pos().y());
                            update(); // force to Repaint
                        });
                    
                        connect(_topRightCircle, &MovableCircle::circleMoved, this, [this](){
                            _topLeftCircle->setPos(_topLeftCircle->pos().x(), _topRightCircle->pos().y());
                            _bottomRightCircle->setPos(_topRightCircle->pos().x(), _bottomRightCircle->pos().y());
                            update(); // force to Repaint
                        });
                    
                        connect(_bottomLeftCircle, &MovableCircle::circleMoved, this, [this](){
                            _topLeftCircle->setPos(_bottomLeftCircle->pos().x(), _topLeftCircle->pos().y());
                            _bottomRightCircle->setPos(_bottomRightCircle->pos().x(), _bottomLeftCircle->pos().y());
                            update(); // force to Repaint
                        });
                    
                        connect(_bottomRightCircle, &MovableCircle::circleMoved, this, [this](){
                            _bottomLeftCircle->setPos(_bottomLeftCircle->pos().x(), _bottomRightCircle->pos().y());
                            _topRightCircle->setPos(_bottomRightCircle->pos().x(), _topRightCircle->pos().y());
                            update(); // force to Repaint
                        });
                    }
                    
                    QRectF BoundedRect::boundingRect() const
                    {
                        // Calculate the Bouding Rect by 4 Limit Points
                        
                        qreal distX = sqrt(pow(_topLeftCircle->x() - _topRightCircle->x(),2) +
                                           pow(_topLeftCircle->y() - _topRightCircle->y(),2)); // eucledian distance
                    
                        qreal distY = sqrt(pow(_topLeftCircle->x() - _bottomLeftCircle->x(),2) +
                                           pow(_topLeftCircle->y() - _bottomLeftCircle->y(),2)); // eucledian distance
                    
                        return QRectF(qMin(_topLeftCircle->pos().x(), _topRightCircle->pos().x()) ,
                                      qMin(_topLeftCircle->pos().y(), _bottomLeftCircle->pos().y()),
                                      distX, distY);
                    }
                    
                    void BoundedRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
                    {
                        Q_UNUSED(option);
                        Q_UNUSED(widget);
                        
                        painter->setBrush(QBrush(Qt::blue));
                        painter->setPen(QPen(Qt::black));
                        painter->drawRect(boundingRect()); // draw by boundingRect
                    }
                    
                    

                    @Computer Science Student - Brazil
                    Web Developer and Researcher
                    “Sometimes it’s the people no one imagines anything of who do the things that no one can imagine.” - Alan Turing

                    enjoysmath 2 Replies Last reply Reply Quote 4
                    • enjoysmath
                      enjoysmath @KillerSmath last edited by

                      @KillerSmath

                      Thank you, I can definitely look through and understand your code.

                      https://github.com/enjoysmath
                      https://math.stackexchange.com/users/26327/exercisingmathematician

                      1 Reply Last reply Reply Quote 0
                      • enjoysmath
                        enjoysmath @KillerSmath last edited by

                        @KillerSmath

                        Sadly, this doesn't resolve my issues. I'm still troubleshooting it.

                        Remember, I want pressing on the items to center the item on the mouse cursor.

                        https://github.com/enjoysmath
                        https://math.stackexchange.com/users/26327/exercisingmathematician

                        KillerSmath 1 Reply Last reply Reply Quote 0
                        • KillerSmath
                          KillerSmath @enjoysmath last edited by

                          @enjoysmath
                          We need more information...

                          1. Your image is very polluated and confused
                          2. Try to describe what you pretent to achieve with this manipulation and maybe it can be clearier to understand.

                          @Computer Science Student - Brazil
                          Web Developer and Researcher
                          “Sometimes it’s the people no one imagines anything of who do the things that no one can imagine.” - Alan Turing

                          1 Reply Last reply Reply Quote 0
                          • First post
                            Last post