I add Items to a GraphicsScene which should appear in a fix size on screen independent of zooming in and out. I create them in the following manner:
class LocationGraphicsItem(QtWidgets.QGraphicsEllipseItem):
def __init__(self, coordinate, parent = None):
super().__init__(parent=parent)
x = coordinate[0]
y = -coordinate[1]
self.setRect(-5, -5, 10, 10)
self.setPos(x, y)
self.setFlag(self.GraphicsItemFlag.ItemIgnoresTransformations)
I add them to the scene like this:
coordinate = (3412770.9, 5358376.3)
item = LocationGraphicsItem(coordinate)
my_scene.addItem(item)
item.ensureVisible()
and later call
my_view.fitInView(my_scene.sceneRect(), Qt.KeepAspectRatio)
on my view.
As long as I add all of them before the Qt eventloop is started, the view is set to display the correct scope, the scene figures out the correct sceneRect
which is something like:
PySide6.QtCore.QRectF(3412765.400000, -5371895.600000, 82420.600000, 13524.800000)
However, if I add items after the eventloop is started and the QGraphicsView
is already visible, my region of interest is cramped in the top right corner, as the sceneRect
changes to:
PySide6.QtCore.QRectF(-5.500000, -5371895.600000, 3495191.500000, 5371901.100000)
How can I avoid that? Can someone explain to me why this happens?
I know that I can calculate the relevant rectangle newly with my_scene.itemsBoundingRect()
, which unfortunately is quite slow.
What are alternatives / best practices to implement a fixed-size marker? I would prefer to specify the size in pixels anyway.
Here's some complete code:
import random
import sys
print("Python: ", sys.version)
import PySide6
print("PySide: ", PySide6.__version__)
from PySide6 import QtCore, QtGui, QtWidgets
class LocationGraphicsItem(QtWidgets.QGraphicsEllipseItem):
def __init__(self, coordinate, parent = None):
super().__init__(parent=parent)
x = coordinate[0]
y = -coordinate[1]
self.setRect(-5, -5, 10, 10)
self.setPos(x, y)
self.setBrush(QtGui.QColor("blue"))
self.setFlag(self.GraphicsItemFlag.ItemIgnoresTransformations)
def add_something():
print(scene.sceneRect())
point =(3.4e6+random.random()*1e5, 5.3e6+random.random()*1e5)
print("Random point: ", point)
item = LocationGraphicsItem(point)
scene.addItem(item)
item.setVisible(True)
item.ensureVisible()
view.fitInView(scene.sceneRect(), QtCore.Qt.KeepAspectRatio)
QtWidgets.QApplication.processEvents()
print(scene.sceneRect())
if __name__ == "__main__":
my_points = [(3412770.9, 5358376.3), (3495180.5, 5371890.1), (3495099.1, 5370624.6), (3485765.4, 5371030.1)]
app = QtWidgets.QApplication()
scene = QtWidgets.QGraphicsScene()
for point in my_points:
item = LocationGraphicsItem(point)
scene.addItem(item)
item.setVisible(True)
item.ensureVisible()
window = QtWidgets.QMainWindow(parent = None)
window.setGeometry(50, 50, 1300, 750)
basic_widget = QtWidgets.QWidget(parent=window)
window.setCentralWidget(basic_widget)
layout = QtWidgets.QHBoxLayout()
basic_widget.setLayout(layout)
button = QtWidgets.QPushButton("add something", parent = basic_widget)
button.clicked.connect(add_something)
layout.addWidget(button)
view = QtWidgets.QGraphicsView(scene, parent = basic_widget)
layout.addWidget(view)
view.fitInView(scene.sceneRect(), QtCore.Qt.KeepAspectRatio)
window.setVisible(True)
app.exec()
Output:
Python: 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)]
PySide: 6.4.0.1
PySide6.QtCore.QRectF(3412765.400000, -5371895.600000, 82420.600000, 13524.800000)
Random point: (3449076.7327629146, 5323846.465410875)
PySide6.QtCore.QRectF(-5.500000, -5371895.600000, 3495191.500000, 5371901.100000)
It seems to be a Qt bug. Why not report it? (See Report Bugs.)
You can remedy it by calling the QGraphicsItem.sceneTransform()
manually like the following.
def add_something():
...
item = LocationGraphicsItem(point)
scene.addItem(item)
...
dummy = item.sceneTransform()
view.fitInView(scene.sceneRect(), QtCore.Qt.KeepAspectRatio)
...