Skip to content

How to write correctly PyQT5 event function

How can I correctly write the event function to pass from one QLineEdit to another one by pressing the enter key?

I know how to do that in this way:

Working Example

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QFormLayout
from PyQt5.QtCore import Qt, QEvent
class working(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
    def initUI(self):
        self.show()
        self.x = QLineEdit(self)
        self.x1 = QLineEdit(self)
        layout = QFormLayout(self)
        layout.addRow("x:", self.x)
        layout.addRow("x1:", self.x1)
    def event(self, event):
        if event.type() == QEvent.KeyPress:
            if event.key() in (Qt.Key_Return, Qt.Key_Enter):
                self.focusNextPrevChild(True)
        return super().event(event)
def main():
    app = QApplication(sys.argv)
    ex = working()
    sys.exit(app.exec_())
if __name__ == '__main__':
    main()

Now i want to understand how to do the same with this code (i think that the problem is with super() and init but i don’t know why).

UNWorking Basic Example

from PyQt5 import QtCore, QtWidgets, QtGui
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(193, 119)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.x = QtWidgets.QLineEdit()
        self.verticalLayout_2.addWidget(self.x)
        self.x1 = QtWidgets.QLineEdit()
        self.verticalLayout_2.addWidget(self.x1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "0"))
    def event(self, event):
        if event.type() == QEvent.KeyPress:
            if event.key() in (Qt.Key_Return, Qt.Key_Enter):
                self.focusNextPrevChild(True)
        return super().event(event)
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Answer

The second example does not work because you’re trying to override the event method, which is a method available only for QWidget subclasses, while your Ui_MainWindow is a simple python object subclass, so it will never be called.

The event() method is called by Qt on any QWidget, and since you are overriding it in the first example, your method is actually called. In the second example you’re not overriding the QWidget event, but you’re just creating a function that is never called by anything.

Theoretically you could overwrite the event method of the MainWindow object in setupUi (that’s what’s called “monkey patching”):

    def setupUi(self, MainWindow):
        # ...
        MainWindow.event = self.event

But that won’t work as the self you’re referring to in that function is actually the Ui_MainWindow instance, not the actual QMainWindow instance. While you could use a lambda, and add the argument with the MainWindow instance, I’d strongly advise against that, and the following explanation will clarify why you shouldn’t do it.


There’s no point in doing what you’re trying to achieve, for a simple reason: modifying or trying to mimic the behavior of `pyuic` generated files is something that should **NEVER** be done.

Editing is discouraged mostly because whenever you need to change something in the UI from Designer, you’ll end up trying to integrate the new generated code (hoping that you’ve not overwritten the file in the meantime) with the existing one, which will normally result in tons of errors, issues, crashes and headaches.

Editing or mimicking is discouraged as it makes very hard to implement simple class operations or override existing methods. Also, while I can understand you’re interest in studying how it works, except for reading how the interface is created within the setupUi, there’s nothing more to learn there. The pyuic files are intended only as an “utility layer” to ease up programming in PyQt, they should always only be imported and used as suggested in the official guidelines about using Designer.

If you want to implement the event of the first example, you’ll need to use your own subclass and use one of the approaches suggested in the link above. The most common and easy to use is the multiple inheritance approach:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QLineEdit, QFormLayout
from PyQt5.QtCore import Qt, QEvent
from ui_mainwindow import Ui_MainWindow
class Working(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
    def event(self, event):
        if event.type() == QEvent.KeyPress:
            if event.key() in (Qt.Key_Return, Qt.Key_Enter):
                self.focusNextPrevChild(True)
        return super().event(event)
if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    mainWindow = Working()
    mainWindow.show()
    sys.exit(app.exec_())

Note: I’m assuming that you’ve generated the file again with pyuic and that is named ui_mainwindow.py; also, note that if you’re using a QMainWindow in designer, you must subclass from the same class (QMainWindow, not QWidget); finally, since you’re already subclassing from QMainWindow and Ui_MainWindow, you have to create an instance of Working, not one of QMainWindow, nor one of Ui_MainWindow.

Another possibility is to use the uic module, which allows to import the .ui file directly without the need to rebuild the whole interface everytime.

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QLineEdit, QFormLayout
from PyQt5.QtCore import Qt, QEvent
from PyQt5.uic import loadUi
class Working(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        loadUi('mainWindow.ui', self)
        # ...