Wednesday, 29 February 2012

PyQt Signals and Slots

To capture events generated by GUI-elements in PyQt the signal and slots mechanism from Qt is used:

Signals: signals are emitted when a user interacts with a Qt widget (e.g. the user clicks a button).

Slots: slots can be connected to signals and act upon receiving the signal.

It is possible to connect multiple slots to one signal.

With PyQt there are three different ways one can connect a slot to a signal:

The classic way:

To connect a slot to a signal in the classical Qt way the QObject's connect method is used:

object.connect(signal_widget, signal, slot_widget, slot)


import sys
from PyQt4 import QtCore, QtGui

def slot_function():
    """This function will be triggered when the button is clicked."""
    print("The function was triggered.")

if __name__ == '__main__':
    application = QtGui.QApplication(sys.argv)
    window = QtGui.QWidget()
    window.size = QtCore.QSize(20, 20)
    # This button will emit the signal when it is clicked.
    signal_button = QtGui.QPushButton("Click here", window)

    # Connect the button-click signal to the slot_function we defined earlier.
    QtCore.QObject.connect(signal_button, QtCore.SIGNAL("clicked()"), slot_function)
    window.show()
    application.exec_()

The signal that is emitted is specified via the QtCore.SIGNAL()-method. In this example no slot is specified, because our self-defined function shall be executed when the signal is received. By specifying a slot it is possible to declare which slot the slot_object will execute upon receiving the signal.

There are, however two more ways that were introduced by PyQt: New style signal and slots

New style signals and slots:

Connecting a signal to a slot with the new style system is easy and only uses the object that will emit the signal:

object.signal.connect(function)

In the example above the QtCore.QObject.connect(...)-method would be replaced with:

signal_button.clicked.connect(slot_function)

Moreover it is also possible to autoconnect slots using Qt - this is especially helpful when using the Qt-Designer, but it can easily be activated by hand as well.

Autoconnecting slots and signals:

However, it will only work when the slots that shall be autoconnected are either defined in the same object that will emit the signal or in a child-object.

To autoconnect the objects the slots need to adhere to the following form:

on_<object name>_<signal name>(<signal parameters>)

To adjust the example to this signals/slots connection the MainWindow containing the button and the slot-function are moved into a class:


import sys
from PyQt4 import QtCore, QtGui 
class MainWindow(QtGui.QMainWindow):
    """A new window for the application."""
    def __init__(self):
        super(MainWindow, self).__init__()
        self.size = QtCore.QSize(20, 20)
        # This button will emit the signal when it is clicked.
        self.signal_button = QtGui.QPushButton("Click here", self)
        # This name is used to identify the  in the function.
        self.signal_button.setObjectName("signal_button")
        # This will wire up the signals and slots depending on names.
        QtCore.QMetaObject.connectSlotsByName(self)

    @QtCore.pyqtSlot()
    def on_signal_button_clicked(self):
        """This function will be triggered when the button is clicked."""
        print("The function was triggered.")

def main():
    """docstring for main"""
    application = QtGui.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    application.exec_()

if __name__ == '__main__':
    main()

This is all there is to autoconnecting slots and signals - when using the Qt Designer to create GUIs all the setup (setting an object name and using connectSlotsByName) are called inside the generated code of pyuic4 so you don't have to do this yourself.

No comments:

Post a Comment