Search code examples
pyqtqgraphicsscenepyqt6

How to intercept QGraphicsSceneMoveEvent, change event position and call super().mouseMoveEvent()?


I'm happy of QGraphicsItem.ItemIsMovable to move a QGraphicsRectItem() with the mouse. If the Control Key is pressed down, I would like it to move only vertically = keep mouse the position x() a fixed value. To keep it simple, below code doesn't show the use of 'Control Key'

My idea is to inherit QGraphicsScene(), overload mouseMoveEvent(), change event.pos().x() to a fixed value, and call super() with modified event.

import sys
from PyQt5.QtWidgets import *

class QGraphicsScene(QGraphicsScene):
    def mouseMoveEvent(self, event):
        newEvent = event
       #newEvent.setPos(  100.0, event.pos().y() ) #### HERE, I need help ! !
        super().mouseMoveEvent(newEvent)

class MainWindow(QWidget):
    def __init__(self ):
        super(QWidget, self).__init__()

        self.scene = QGraphicsScene()
        self.scene.setSceneRect( 0.0, 0.0, 640.0, 480.0 )

        view= QGraphicsView()
        view.setScene(self.scene)
        layout = QVBoxLayout()
        layout.addWidget( view )
        self.setLayout( layout )

        self.myRect= QGraphicsRectItem( 0,  0, 100, 100)
        self.scene.addItem( self.myRect )

        self.myRect.setFlag( QGraphicsItem.ItemIsMovable,      True  )

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec_()

If I will un-comment the line 7 :

    newEvent.setPos(  100.0, event.pos().y() )

I will have this error :

AttributeError: 'QGraphicsSceneMouseEvent' object has no attribute 'setPos'

How can I do ? Thanks.


Solution

  • Item movements can be restricted by setting the ItemSendsGeometryChanges flag and by implementing their itemChange() and checking for changes of type ItemPositionChange, which are sent before the item is moved:

    class VerticallyBlockedRect(QGraphicsRectItem):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.setFlag(self.GraphicsItemFlag.ItemSendsGeometryChanges)
    
        def itemChange(self, change, value):
            if change == self.GraphicsItemChange.ItemPositionChange:
                value.setY(self.y())
            return super().itemChange(change, value)
    

    You will obviously need to implement the above in order to prevent the movement only when required.