happy_code_egg
happy_code_egg

Reputation: 750

PyQt5: I can't understand QGraphicsScene's setSceneRect(x, y, w, h)

I see some people say if you want to put QGraphicsScene's origin of coordinates at the origin of QGraphicsView, i.e. top-left corner. You need to let both of them have the same size.

So here is what I do:

import sys
from PyQt5.QtWidgets import QApplication, QGraphicsLineItem, 
QGraphicsScene, QGraphicsView


class Demo(QGraphicsView):
    def __init__(self):
        super(Demo, self).__init__()
        self.resize(300, 300)

        self.line = QGraphicsLineItem()
        self.line.setLine(0, 0, 100, 100)

        self.scene = QGraphicsScene()
        self.scene.setSceneRect(0, 0, 300, 300)
        self.scene.addItem(self.line)

        self.setScene(self.scene)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

The View's size is 300x300 and I use setSceneRect() to make sure the Scene's size is 300x300.

In this case, the Scene's origin is at top-left corner. enter image description here

However, when I use setSceneRect(0, 0, 150, 150), the origin is not there, but at (75, 75)! enter image description here

Why? I thought the first two parameters of setSceneRect(x, y, w, h) set where the origin of coordinates should be. When the Scene is smaller than View, how can we make sure the Scene's origin is at the top-left corner?

Any help would be appreciated!

Upvotes: 6

Views: 4359

Answers (1)

eyllanesc
eyllanesc

Reputation: 244301

As the docs point out:

alignment : Qt::Alignment

This property holds the alignment of the scene in the view when the whole scene is visible.

If the whole scene is visible in the view, (i.e., there are no visible scroll bars,) the view's alignment will decide where the scene will be rendered in the view. For example, if the alignment is Qt::AlignCenter, which is default, the scene will be centered in the view, and if the alignment is (Qt::AlignLeft | Qt::AlignTop), the scene will be rendered in the top-left corner of the view.

So, by default, the scenerect is centered with the viewport of the QGraphicsView, and in the case of having the same size, the behavior you point out is observed, but in the second case the property of the centering is highlighted.

So the solution is to establish the alignment to:

import sys
from PyQt5.QtWidgets import QApplication, QGraphicsLineItem, QGraphicsScene, QGraphicsView
from PyQt5.QtCore import Qt

class Demo(QGraphicsView):
    def __init__(self):
        super(Demo, self).__init__()
        self.resize(300, 300)

        self.line = QGraphicsLineItem()
        self.line.setLine(0, 0, 100, 100)

        self.scene = QGraphicsScene()
        self.scene.setSceneRect(0, 0, 150, 150)
        self.scene.addItem(self.line)

        self.setScene(self.scene)
        self.setAlignment(Qt.AlignTop | Qt.AlignLeft)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

enter image description here


Explanation:

To understand what the scenerect is first, it must be understood that it is the QGraphicsView and the QGraphicsScene, these concepts are explained with an analogy to the recording of a movie, the QGraphicsView would be the camera, the QGraphicsScene represents what is recorded, ie the scene. The scene is delimited by the sceneRect, if the camera is very close to the scene, its limits will not be observed, but if the camera is far away, the scenerect projection in the camera will not occupy the whole screen, so it will have to be aligned in some position, in the case of QGraphicsView the alignment property is used.


why the scene is no longer centered in the view if I use setSceneRect(50, 50, 150, 150)?

To answer I use the following example where to make the scenerect visible I use a QGraphicsRectItem:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class Demo(QtWidgets.QGraphicsView):
    def __init__(self):
        super(Demo, self).__init__()
        self.resize(300, 300)

        self.line = QtWidgets.QGraphicsLineItem()
        self.line.setLine(0, 0, 100, 100)

        self.scene = QtWidgets.QGraphicsScene()
        self.scene.setSceneRect(50, 50, 150, 150)
        self.scene.addItem(self.line)
        rect_item = self.scene.addRect(QtCore.QRectF(50, 50, 150, 150))
        rect_item.setPen(QtGui.QPen(QtGui.QColor("green")))

        self.setScene(self.scene)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    demo = Demo()
    demo.show()
    sys.exit(app.exec_())

enter image description here

As you can see the alignment is not about QRectF(0, 0, w, h) but the center of QRectF(x, y, w, h) which in this case is (100,100). So keep centered on sceneRect in the QGraphicsView.

Upvotes: 5

Related Questions