[Python] How does QGraphicsItems interact with QGraphicsScene coordinates?

Discussão em 'Python' iniciado por Stack, Junho 27, 2025.

  1. Stack

    Stack Membro Participativo

    I am confused by Qt's coordinate handling in a QGraphicsScene. I'm using PySide6, and create a QGraphicsScene with movable QGraphicsRectItems. I would like to limit the items to the boundaries of the scene. There is an example in the PySide docs, but it does not work (the example is auto-translated from C++ to Python, but it does not work for me even after correcting the translation errors).

    I figured the general idea would be to monitor an item for movement, which can be done by implementing the itemChange event handler. A pseudo implementation (not bothering with the nitty gritty Qt details) would look like:

    def itemChange(newPosition):
    if newPosition.x < scene.x:
    newPosition.x = scene.x
    return newPosition


    I cannot figure out how to make this actually work. It seems like every graphics item has its own idea of origin, and I have not found the mapping function that will turn the item's coordinates into scene coordinates. I have tried mapToScene and mapFromItem. But

    I have reviewed several different questions on stackoverflow, but I still seem to be misunderstanding what's happening.

    A minimal working example with a square which prints out the X coordinates when the square is dragged. As you drag the rectangle to the left of the scene, I would expect at least one of the X values to approach zero.

    import sys
    from PySide6 import QtWidgets
    from PySide6.QtCore import QPointF, QRectF
    from PySide6.QtGui import QBrush, QColor, QPen
    from PySide6.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsRectItem, QGraphicsItem, QGraphicsView


    class BoundableItem(QGraphicsRectItem):
    def __init__(self, *args, **kwargs):
    super(BoundableItem, self).__init__(*args, **kwargs)
    # Our items are movable and they send geometry change events
    self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
    self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges)

    def itemChange(self, change, value: QPointF):
    if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange and self.scene():
    print(f"X=> As passed: {value.x()} mapToScene: {self.mapToScene(value).x()} mapFromItem: {self.mapFromItem(self, value).x()}")
    return super(BoundableItem, self).itemChange(change, value)


    class MyWindow(QMainWindow):
    def __init__(self):
    super(MyWindow, self).__init__()
    # Basic window setup
    self.setWindowTitle("Bounding Test")
    self.setFixedSize(320, 320)
    # Set up a fixed-sized screen
    scene = QGraphicsScene(0, 0, 240, 240)
    scene.setBackgroundBrush(QBrush(QColor.fromRgb(24, 24, 48)))
    # Draw a border around the scene
    scene.addRect(scene.sceneRect(), QPen(QColor.fromRgb(255, 255, 255)))
    # A blue square, offset by 32
    rect = BoundableItem(QRectF(32.0, 32.0, 32.0, 32.0))
    rect.setBrush(QBrush(QColor.fromRgb(0, 0, 255)))
    scene.addItem(rect)

    # Make a graphics view for the scene
    self.view = QGraphicsView()
    self.view.setScene(scene)
    self.setCentralWidget(self.view)


    if __name__ == '__main__':
    app = QApplication()
    window = MyWindow()
    window.show()
    sys.exit(app.exec())




    Here is an example, with the console output, after dragging the square out of the bounds:

    [​IMG]

    X=> As passed: -40.0 mapToScene: -79.0 mapFromItem: -40.0

    To actually change what happens in itemChange(), I know that you need to return the desired position (QPointF), I just didn't do that for my example because I was trying to just print out the X coordinates.

    Continue reading...

Compartilhe esta Página