Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. 5 days. I have been trying to center a freakin QGraphicsItem hierarchy. They made this hard.
QtWS25 Last Chance

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

Scheduled Pinned Locked Moved Unsolved General and Desktop
12 Posts 4 Posters 2.5k Views
  • 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.
  • enjoysmathE Offline
    enjoysmathE Offline
    enjoysmath
    wrote on last edited by enjoysmath
    #1

    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-worxR 1 Reply Last reply
    0
    • enjoysmathE 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_())
      
      
      raven-worxR Offline
      raven-worxR Offline
      raven-worx
      Moderators
      wrote on last edited by
      #2

      @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

      enjoysmathE 1 Reply Last reply
      0
      • enjoysmathE Offline
        enjoysmathE Offline
        enjoysmath
        wrote on last edited by
        #3

        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
        0
        • raven-worxR raven-worx

          @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.

          enjoysmathE Offline
          enjoysmathE Offline
          enjoysmath
          wrote on last edited by
          #4

          @raven-worx

          How is the code above?

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

          raven-worxR 1 Reply Last reply
          0
          • enjoysmathE enjoysmath

            @raven-worx

            How is the code above?

            raven-worxR Offline
            raven-worxR Offline
            raven-worx
            Moderators
            wrote on last edited by
            #5

            @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

            enjoysmathE 2 Replies Last reply
            0
            • raven-worxR raven-worx

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

              enjoysmathE Offline
              enjoysmathE Offline
              enjoysmath
              wrote on last edited by
              #6

              @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
              0
              • raven-worxR raven-worx

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

                enjoysmathE Offline
                enjoysmathE Offline
                enjoysmath
                wrote on last edited by enjoysmath
                #7

                @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

                KillerSmathK 1 Reply Last reply
                0
                • enjoysmathE 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()

                  KillerSmathK Offline
                  KillerSmathK Offline
                  KillerSmath
                  wrote on last edited by
                  #8

                  @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

                  enjoysmathE S 3 Replies Last reply
                  4
                  • KillerSmathK KillerSmath

                    @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
                    }
                    
                    
                    enjoysmathE Offline
                    enjoysmathE Offline
                    enjoysmath
                    wrote on last edited by
                    #9

                    @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
                    0
                    • KillerSmathK KillerSmath

                      @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
                      }
                      
                      
                      enjoysmathE Offline
                      enjoysmathE Offline
                      enjoysmath
                      wrote on last edited by
                      #10

                      @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

                      KillerSmathK 1 Reply Last reply
                      0
                      • enjoysmathE enjoysmath

                        @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.

                        KillerSmathK Offline
                        KillerSmathK Offline
                        KillerSmath
                        wrote on last edited by
                        #11

                        @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
                        0
                        • KillerSmathK KillerSmath

                          @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
                          }
                          
                          
                          S Offline
                          S Offline
                          StudentScripter
                          wrote on last edited by
                          #12

                          @KillerSmath Your example is great, exactly what i was looking for, but nevertheless, i noticed that this system breaks when there is rotation involved. I'm pretty new with all this but may you or anyone else can hint me the way on how get it working with rotation? Thanks alot beforehand. :)

                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved