Qt mouse events

I’ve just started to get into Qt for Maya, using PySide. I’m developing a node interface and have a small problem which I haven’t been able to solve yet. If you think you know of a solution but doesn’t speak Python, just write it in c++ and I’ll try to convert it :slight_smile:

I have a QGraphicsView which manages a QGraphicsScene. The QGraphicsScene have all the nodes (QGraphicsItem) in it. The QGraphicsItem have a built in function, or flag if you like, called “ItemIsMovable” that I use to be able to move the nodes around by left clicking on a node and moving the mouse. This works well until I try to implement a way of navigating the node interface. When I implement overrides for mouseMoveEvent, mousePressEvent and mouseReleaseEvent in the QGraphicsView class, to be able to pan and zoom, the functionality for moving the nodes them self stops working. It is as if my overrides to the QGraphicsView class prevents any similar events from triggering on the QGraphicsItem.

As an example, this is what my mousePressEvent function in my QGraphicsView class looks like:


def mousePressEvent(self, event):
    # Panning
    if (event.button() == QtC.Qt.MidButton) and (self.alt == 1) and (self.panning == 0): # self.alt triggers when the "Alt" button is pressed
        self.mousePressPos = QtC.QPointF(event.pos())
        self.scrollBarValuesOnMousePress.setX(self.horizontalScrollBar().value())
        self.scrollBarValuesOnMousePress.setY(self.verticalScrollBar().value())
        self.panning = 1
        event.accept()
    else:
        event.ignore()
        return

Thanks in advance!
/Gabriel

As an aside, please use 1) don’t use ints instead of bools, and 2) don’t even bother comparing to bools. Your line can be written as: if event.button() == QtC.Qt.MidButton and self.alt and not self.panning

As for your question, it is the ‘event.ignore’ that I think is the issue- you’re basically telling Qt to not continue propagating the event down to other widgets. I haven’t dealt with exactly this problem/widget, but you’ll need to allow the events to propagate. I’m pretty sure the docs have a lot to say about this matter (with examples), and since it’s tricky I’m sure there are some answers on Stack Overflow as well. Please tell us what you find! Or maybe post a full example and we can try to fix it.

Thanks for the reminders :slight_smile:
Removing the event.ignore()'s didn’t solve it, I have written a simplified script that reproduces my problem. It doesn’t have it’s own QApplication so copy and paste it in to Maya to try it out.
Alt + middleMouseClick is the combination used for panning.


import PySide.QtGui as QtG
import PySide.QtCore as QtC
import PySide.QtSvg as QtS

class MainWindow(QtG.QMainWindow):
    def __init__(self, parent = None):
        QtG.QMainWindow.__init__(self, parent)
        
        self.initUI()
        
    def initUI(self):
        
        graphicsView = GraphicsView(self)
        self.setCentralWidget(graphicsView)
        
        self.setGeometry(100, 100, 800, 800)
        
        self.show()
        
class GraphicsView(QtG.QGraphicsView):
    def __init__(self, parent):
        QtG.QGraphicsView.__init__(self, parent)
        
        self.nodeEditorScene = GraphicsScene(self)
        
        self.setScene(self.nodeEditorScene)
        
        node01 = DrawNode(True)
        
        self.nodeEditorScene.addItem(node01)
        
        # This makes the node movable
        node01.setFlag(QtG.QGraphicsItem.ItemIsMovable)
        
        # Used for panning
        self.mousePressPos = QtC.QPointF()
        self.scrollBarValuesOnMousePress = QtC.QPointF()
        self.alt = False
        self.panning = False
        
    # Commenting out the following mouse events will make the node movable and obviously disable panning
    def mouseMoveEvent(self, event):
        if self.panning and not self.mousePressPos.isNull():
            self.horizontalScrollBar().setValue(self.scrollBarValuesOnMousePress.x() - event.pos().x() + self.mousePressPos.x())
            self.verticalScrollBar().setValue(self.scrollBarValuesOnMousePress.y() - event.pos().y() + self.mousePressPos.y())
            self.horizontalScrollBar().update()
            self.verticalScrollBar().update()
            event.accept()
        
    def mousePressEvent(self, event):
        # Panning
        if event.button() == QtC.Qt.MidButton and self.alt and not self.panning:
            print "mousePress ok!"
            self.mousePressPos = QtC.QPointF(event.pos())
            self.scrollBarValuesOnMousePress.setX(self.horizontalScrollBar().value())
            self.scrollBarValuesOnMousePress.setY(self.verticalScrollBar().value())
            self.panning = True
            event.accept()
        
    def mouseReleaseEvent(self, event):
        if self.panning:
            print "mouseRelease ok!"
            self.mousePressPos = QtC.QPointF()
            self.panning = False
            event.accept()
    
    def keyPressEvent(self, event):
        if event.key() == QtC.Qt.Key_Alt:
            print "keyPress ok!"
            self.alt = True
            event.accept()
            
    def keyReleaseEvent(self, event):
        if event.key() == QtC.Qt.Key_Alt:
            print "keyRelease ok!"
            self.alt = False
            event.accept()
        
class GraphicsScene(QtG.QGraphicsScene):
    def __init__(self, parent):
        QtG.QGraphicsScene.__init__(self, parent)
        
        # Make the scene big to get some room for panning
        self.setSceneRect(-10000, -10000, 20000, 20000)
        
        
class DrawNode(QtG.QGraphicsItem):
    def __init__(self, AA = False, parent = None):
        QtG.QGraphicsItem.__init__(self, parent)
        
        self.newPos = QtC.QPointF()
        self.setZValue(1)
        
        self.penWidth = 1
        self.hSize = 360
        self.vSize = 400
        
        # Specify the bounding box rectangle
        self.qrectf = QtC.QRectF((-1 * (self.hSize / 2)) - self.penWidth / 2, (-1 * (self.vSize / 2)) - self.penWidth / 2, self.hSize + self.penWidth / 2, self.vSize + self.penWidth / 2)
        
    # Just for debugging
    def mousePressEvent(self, event):
        print "mousePress event registered on the node itself!"
        event.accept()
        
    # Implements its pure virtual function boundingRect, to define the bounding box
    def boundingRect(self):
        return self.qrectf
    
    # Implements its pure virtual function paint, to draw the item
    def paint(self, painter, option, widget):
        self.draw_nodeBackground(painter)
        
    def draw_nodeBackground(self, painter):
        QRectF_nodeBackground = self.qrectf
        bgBrushColor = QtG.QColor(117, 117, 117, 255)
        bgBrush = QtG.QBrush(bgBrushColor)
        painter.setBrush(bgBrush)
        painter.setPen(QtC.Qt.NoPen) #Don't draw the outline
        painter.drawRoundedRect(QRectF_nodeBackground, 15, 15)
        
ex = MainWindow()
ex.show()

I’m using PyQt but experienced same issue.
Try re-sending mouse event at the end of mouse function.
eg.

def mousePressEvent(self, event):
    # Panning
    if (event.button() == QtC.Qt.MidButton) and (self.alt == 1) and (self.panning == 0): # self.alt triggers when the "Alt" button is pressed
        self.mousePressPos = QtC.QPointF(event.pos())
        self.scrollBarValuesOnMousePress.setX(self.horizontalScrollBar().value())
        self.scrollBarValuesOnMousePress.setY(self.verticalScrollBar().value())
        self.panning = 1
        event.accept()
    else:
        event.ignore()
        return
    QtGui.QGraphicsView.mousePressEvent(self, event) # <-- added this line.

It worked! Thank you very much hslth! :smiley:

Actually event.accept() method stops event propagation. UI events get propagated up to ascending widgets until one of them is willing to “accept” it :slight_smile:

Thanks for that madyasiwi, it’s good to know :slight_smile: