Don Smythe Don Smythe - 1 month ago 16
Python Question

PyQt4 signals and slots with eventFilter

I am having trouble receiving a signal sent from an event filter. In the example below the button pressed signal/slot works fine, and the focus out filter signals emit OK. However the focus out signals are not being intercepted and the slots do not fire. Any idea what I am doing wrong?

from PyQt4.QtCore import SIGNAL, QObject, QEvent
from PyQt4.QtGui import QApplication, QLabel, QWidget, QLineEdit, QPushButton, QTextEdit, QVBoxLayout

class SignalOnFocus(QWidget):
def __init__(self):
super(SignalOnFocus, self).__init__()
layout = QVBoxLayout()
self.label = QLabel("Type in some text then push button")
self.inputLineEdit1 = QLineEdit()
self.inputLineEdit1.setObjectName("inputLineEdit1")

self.focusOutFilter = FocusOutFilter()
self.inputLineEdit1.installEventFilter(self.focusOutFilter)
self.connect(self.inputLineEdit1, SIGNAL("focus_out"),
self.focusLost)
self.inputLineEdit2 = QLineEdit()
self.inputLineEdit2.setObjectName("inputLineEdit2")
self.mousePressedFilter = MousePressedFilter()
self.inputLineEdit2.installEventFilter(self.mousePressedFilter)
self.connect(self.inputLineEdit2, SIGNAL("mouse_clicked"), self.mouseClicked)
self.button1 = QPushButton("Press me")
self.button1.setObjectName("button1")
self.connect(self.button1, SIGNAL("clicked()"), self.buttonPressed)
self.textEdit = QTextEdit()
layout.addWidget(self.label)
layout.addWidget(self.inputLineEdit1)
layout.addWidget(self.inputLineEdit2)
layout.addWidget(self.button1)
layout.addWidget(self.textEdit)
self.setLayout(layout)

def mouseClicked(self):
self.textEdit.append(" mouse clicked")

def buttonPressed(self):
self.textEdit.append(" button pressed")

def focusLost(self):
self.textEdit.append(" focus_out")

class MousePressedFilter(QObject):
def eventFilter(self, widget, event):
if event.type() == QEvent.MouseButtonPress:
print("--eventFilter() mouse_clicked on "+str(widget.objectName()))
self.emit(SIGNAL("mouse_clicked"))
return False
else:
return False

class FocusOutFilter(QObject):
def eventFilter(self, widget, event):
if event.type() == QEvent.FocusOut:
print("--eventFilter() focus_out on "+str(widget.objectName()))
self.emit(SIGNAL("focus_out"))
return False
else:
return False

if __name__ == "__main__":
app = QApplication([])
form = SignalOnFocus()
form.show()
app.exec_()

Answer

The filter objects are emitting the signals, so that is what you need to specify when connecting them:

    self.connect(self.focusOutFilter, SIGNAL("focus_out"), self.focusLost)
    ...
    self.connect(self.mousePressedFilter, SIGNAL("mouse_clicked"), self.mouseClicked)

But please seriously consider getting rid of that ugly, old-style syntax for connecting signals. Official support for Qt4 is coming to an end this year, and PyQt5 has already made the old-style syntax totally obsolete.

Using the new-style syntax, your example would look like this:

from PyQt4.QtCore import pyqtSignal, QObject, QEvent

class SignalOnFocus(QWidget):
    def __init__(self):
        ...    
        self.focusOutFilter = FocusOutFilter()
        self.inputLineEdit1.installEventFilter(self.focusOutFilter)
        self.focusOutFilter.focusOut.connect(self.focusLost)

class FocusOutFilter(QObject):
    focusOut = pyqtSignal()

    def eventFilter(self, widget, event):
        if event.type() == QEvent.FocusOut:
            print("--eventFilter() focus_out on " + widget.objectName())
            self.focusOut.emit()

which I hope you will agree looks much more readable (and easier to get right).

(Also note that if you are using Python 3 with PyQt, by default, any Qt method that returns a QString is automatically converted to a python string - so you don't need to convert it yourself using str).

Comments