2

I am trying to detect a mouse click on my gui, and the following code allows detection of mouse click in 1 layer of Qwidget

import sys
from PySide import QtGui, QtCore

class MouseDetector(QtCore.QObject):
    def eventFilter(self, obj, event):
        if event.type() == QtCore.QEvent.MouseButtonPress:
            print 'mouse pressed', obj
        return super(MouseDetector, self).eventFilter(obj, event)

class MainWindow(QtGui.QWidget):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        layout = QtGui.QHBoxLayout()
        layout.addWidget(QtGui.QLabel('this is a label'))
        layout.addWidget(QtGui.QPushButton('Button'))

        self.setLayout(layout)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)  
    mouseFilter = MouseDetector()
    app.installEventFilter(mouseFilter)

    main = MainWindow()
    main.show()

    sys.exit(app.exec_())

However if I have a Qwidget embedded within Qwidget and within a Qwidget, the mouse clicks do not penetrate through the app.

Not only that, what is confusing is that when I install Event filter to the inner widget, the mouse clicks still don't get detected at all.

# Widget x.1 is embedded in Widget X

#    -----------------Widget x.1-----------------------
#    |                                                |
#    |                                                |
#    | ----------------------  ---------------------| |
#    | |                    |  |                    | |
#    | |   Widget  x.1.1 |  |  |   Widget x.1.2     |
#    | |                    |  |                    | |
#    | ----------------------  ---------------------| |
#    |                                                |
#    --------------------------------------------------

Am I approaching the solution wrong? Any advice would be much appreciated.

Hannele
  • 9,301
  • 6
  • 48
  • 68
Kevin
  • 83
  • 2
  • 6

1 Answers1

1

This is the expected behavior of mouse event propagation in Qt.

QLabel and QPushButton accept mouse events by default, consuming them and not letting them propagate to the parent. You could inherit your QWidget and overwrite the mouse interaction methods and not accept the events, then they would propagate. You could put a transparent catch mouse events widget on top of everything.

Or you could use your event filter which works. I run the programm and the MouseDetector runs just fine (Windows 7, Python 2.7, PySide 1.2.2):

mouse pressed <PySide.QtGui.QPushButton object at 0x00000000038A0A88>
mouse pressed <PySide.QtGui.QLabel object at 0x00000000038A0908>
mouse pressed <__main__.MainWindow object at 0x00000000038A0888>

A QWidget above everything else that catches mouse events is done easily. For example:

from PySide import QtCore, QtGui

class ClickableWidget(QtGui.QWidget):

    clicked = QtCore.Signal(QtGui.QMouseEvent)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def mousePressEvent(self, event):
        self.clicked.emit(event)
        event.ignore()

def beep():
    print('button clicked')

def dup():
    print('catcher clicked')

app = QtGui.QApplication([])
window = QtGui.QWidget()
button = QtGui.QPushButton('Click me', window)
button.clicked.connect(beep)
catcher = ClickableWidget(window)
catcher.clicked.connect(dup)
catcher.raise_()

window.show()

app.exec_()

Here the button is visible but cannot be clicked because another QWidget (which is transparent by default) is over it (raise_()). Actually even without signalling just putting a widget above it would catch all mouse events because the events are only propagated to parents (and the catching widget is not a child of the button here). The other way (ignoring events) is a bit trickier. See Qt - top level widget with keyboard and mouse event transparency? for example.

Community
  • 1
  • 1
NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
  • Thanks for the answer, can you elaborate on how you would put a transparent catch mouse event widget on top of everything? – Kevin Jul 21 '14 at 23:17
  • @Kevin I elaborated, but you simply need to put any QWidget (which is transparent by default) over all the others. I didn't set a layout or position (because the default size of a QWidget is big enough) but `QStackedLayout` might become handy. – NoDataDumpNoContribution Jul 22 '14 at 12:17