Create a QGraphicsItem at the mouse double-click position

Question:

I’m writing a program that should create a QGraphicsItem when a double-click happens (inside the scene area), and also that item must be created at double-click position. I’ve already written some code, but it doesn’t work properly. When I double-click on the scene, an item gets created, but at a completely different place. So right now that is the problem. I need it to be created at mouse’s position.

Here is my code:

ui_path = "C:/Users/User/Desktop/programming/Creator/Creator.ui"
        
class Creator(QtWidgets.QWidget):
    count = 0
    
    def __init__(self):
        super(Creator, self).__init__()

        loader = QtUiTools.QUiLoader()
        self.ui = loader.load(ui_path, self)
        
        self.variables()

        self.ui.canvas_area.viewport().installEventFilter(self) #canvas_area is the QGraphicsView

        self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.CustomizeWindowHint | Qt.WindowStaysOnTopHint)

    def variables(self):
        self.scene = QtWidgets.QGraphicsScene()
        self.ui.canvas_area.setScene(self.scene)

    def eventFilter(self, obj, event):
        if obj is self.ui.canvas_area.viewport():
            if event.type() == QtCore.QEvent.MouseButtonDblClick:
                self.createItems(event)
        return super(Creator, self).eventFilter(obj, event)

    def createItems(self, event):
        pos = event.pos()
        self._x = pos.x()
        self._y = pos.y()

        rect = self.scene.addRect(self._x, self._y, 40, 40, QPen(Qt.red), QBrush(Qt.gray))
        
        rect.setFlag(QGraphicsItem.ItemIsMovable)
        rect.setFlag(QGraphicsItem.ItemIsFocusable)
        rect.setFlag(QGraphicsItem.ItemIsSelectable)


if __name__ == '__main__':
    creator_window = Creator()
    creator_window.ui.show()

I’ve read something about mapFromScene and mapToScene, and that these can solve the problem – but I don’t really understand how to use them. Also there are some examples of this, but all that I’ve found were in C++, which I know nothing about. So if someone could help me figure out how to solve this problem I would really appreciate that.

Asked By: galaxy

||

Answers:

You first must set the scene-rect for the scene:

self.scene.setSceneRect(0, 0, 1000, 1000)

then you must convert the event position to scene co-ordinates, like this:

pos = self.canvas_area.mapToScene(event.pos())

and that should be all that’s needed to fix your example.


A different approach that may be worth considering would be to install an event-filter on the scene instead:

self.scene.installEventFilter(self)

and then filter on the more specialised graphics events:

def eventFilter(self, obj, event):
    if obj is self.scene:
        if event.type() == QtCore.QEvent.GraphicsSceneMouseDoubleClick:
            self.createItems(event)
    return super(Creator, self).eventFilter(obj, event)

This creates a QGraphicsSceneMouseEvent, which has several useful features that a standard QMouseEvent doesn’t have. This includes scenePos(), which saves having to map to scene co-ordinates all the time, allowing you to then simply do:

pos = event.scenePos()
Answered By: ekhumoro