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.
Forum Updated to NodeBB v4.3 + New Features

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.7k Views 2 Watching
  • 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 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