cooldanietje
cooldanietje

Reputation: 89

PyQT5 setRect moves origin point in a QGraphicsScene

Whenever i try to move a rectangle in a scene, the origin point of the rectangle appears to change to the location where the rectangle was before updating the location.

So if i create the rectangle at (0,0) and move it using rect.setRect(x, y) then returning the position would yield (0,0) instead of (x, y).

If you would move it in the QGraphicsScene using your mouse it would return the correct (x, y).

The code i use to create the rectangle is as following:

class placeableObject:
    def __init__(self, index, scene, QPen, QBrush, width=100, height=100):
        """Parent class for placeable objects"""
        self.width = float(width)
        self.height = float(height)
        self.index = index

        self.rect = scene.addRect(0, 0, int(width), int(height), QPen, QBrush)
        self.rect.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable)

To move this rectangle i have the following embedded function and a function to return the position:

def getPos(self):
    """Returns a list with the x,y position of the object in the scene"""
    return [self.rect.scenePos().x(), self.rect.scenePos().y()]

def move(self, x, y):
    """Moves the object in the editor view to coordinatex x,y"""
    self.rect.setRect(x, y, self.width, self.height)

Upvotes: 1

Views: 713

Answers (2)

musicamante
musicamante

Reputation: 48529

You are forgetting an important aspect of graphics items: their [scene] position is not always the top left corner of the object that is actually shown to the user. This is clear when adding items using scene.add*() (this was already explained on this question).
As the documentation explains:

Note that the item's geometry is provided in item coordinates, and its position is initialized to (0, 0). For example, if a QRect(50, 50, 100, 100) is added, its top-left corner will be at (50, 50) relative to the origin in the item's coordinate system.

The item position is not the rectangle position, so when you use setRect you are not moving the item, but setting a new rectangle at the specified position, while leaving the item at (0, 0) coordinates of its system; note that this also means that if the item has no parent, scenePos() will be the same as pos(), otherwise it's relative to the parent.

If you want to know the actual position of the top left corner of the rectangle, you could use one of the following:

  • item.sceneBoundingRect().topLeft()
  • item.scenePos() + item.rect().topLeft()

If you are always adding the rectangle at (0, 0), then you can just use setPos(), but if you need to compute the position based on the current, actual rectangle position, you have to use one of the functions above.

Note that rectangles can also have negative sizes, so if you need the top left corner of the visible rectangle, you need to normalize it:

    item.scenePos() + item.rect().normalized().topLeft()

Upvotes: 2

cooldanietje
cooldanietje

Reputation: 89

It seems that i have figured it out.

I changed the move function to the following:

def move(self, x, y):
    """Moves the object in the editor view to coordinatex x,y"""
    self.rect.setPos(x, y)

That returns the correct positions within the scene for me! Thanks anyway :)

Upvotes: 0

Related Questions