Qgraphicsview items not being placed where they should be

458 views Asked by At

I recently created a program that will create QgraphicsEllipseItems whenever the mouse is clicked. That part works! However, it's not in the exact place where my cursor is. It seems to be slightly higher than where my mouse cursor is. I do have a QGraphicsRectItem created so maybe the two items are clashing with each other and moving off of one another? How can I get these circles to be placed on top of the rectangle item? Here's the code

class MyView(QtGui.QGraphicsView):
    def __init__(self):
        QtGui.QGraphicsView.__init__(self)

        self.scene = QtGui.QGraphicsScene(self)
        self.item = QtGui.QGraphicsRectItem(400, 400, 400, 400)
        self.scene.addItem(self.item)
        self.setScene(self.scene)
    def paintMarkers(self):
        self.cursor = QtGui.QCursor()
        self.x = self.cursor.pos().x()
        self.y = self.cursor.pos().y()
        self.circleItem = QtGui.QGraphicsEllipseItem(self.x,self.y,10,10)
        self.scene.addItem(self.circleItem)
        self.circleItem.setPen(QtGui.QPen(QtCore.Qt.red, 1.5))
        self.setScene(self.scene)

class Window(QtGui.QMainWindow):
    def __init__(self):
        #This initializes the main window or form
        super(Window,self).__init__()
        self.setGeometry(50,50,1000,1000)
        self.setWindowTitle("Pre-Alignment system")

        self.view = MyView()
        self.setCentralWidget(self.view)
    def mousePressEvent(self,QMouseEvent):
        self.view.paintMarkers()

Much thanks!

1

There are 1 answers

3
three_pineapples On BEST ANSWER

There are two issues with the coordinates you are using to place the QGraphics...Items. The first is that the coordinates from QCursor are global screen coordinates, so you need to use self.mapFromGlobal() to convert them to coordinates relative to the QGraphicsView.

Secondly, you actually want the coordinates relative to the current QGraphicsScene, as this is where you are drawing the item. This is because the scene can be offset from the view (for example panning around a scene that is bigger than a view). To do this, you use self.mapToScene() on the coordinates relative to the QGraphicsView.

I would point out that typically you would draw something on the QGraphicsScene in response to some sort of mouse event in the QGraphicsView, which requires reimplementing things like QGraphicsView.mouseMoveEvent or QGraphicsView.mousePressEvent. These event handlers are passed a QEvent which contains the mouse coordinates relative to the view, and so you don't need to do the global coordinates transformation I mentioned in the first paragraph in these cases.


Update

I've just seen your other question, and now understand some of the issue a bit better. You shouldn't be overriding the mouse event in the main window. Instead override it in the view. For example:

class MyView(QtGui.QGraphicsView):
    def __init__(self):
        QtGui.QGraphicsView.__init__(self)

        self.scene = QtGui.QGraphicsScene(self)
        self.item = QtGui.QGraphicsRectItem(400, 400, 400, 400)
        self.scene.addItem(self.item)
        self.setScene(self.scene)
    def paintMarkers(self, event):
        # event position is in coordinates relative to the view
        # so convert them to scene coordinates
        p = self.mapToScene(event.x(), event.y())
        self.circleItem = QtGui.QGraphicsEllipseItem(0,0,10,10)
        self.circleItem.setPos(p.x()-self.circleItem.boundingRect().width()/2.0, 
                               p.y()-self.circleItem.boundingRect().height()/2.0)
        self.scene.addItem(self.circleItem)
        self.circleItem.setPen(QtGui.QPen(QtCore.Qt.red, 1.5))
        # self.setScene(self.scene) # <-- this line should not be needed here

    # Note, I've renamed the second argument `event`. Otherwise you locally override the QMouseEvent class
    def mousePressEvent(self, event):
        self.paintMarkers(event)
        # you may want to preserve the default mouse press behaviour,
        # in which case call the following
        return QGraphicsView.mousePressEvent(self, event)

Here we have not needed to use QWidget.mapFromGlobal() (what I covered in the first paragraph) because we use a mouse event from the QGraphicsView which returns coordinates relative to that widget only.


Update 2

Note: I've updated how the item is created/placed in the above code based on this answer.