Connecting signals and slots

#!/usr/bin/env python
#
# [SNIPPET_NAME: Connecting signals and slots]
# [SNIPPET_CATEGORIES: PyQt4]
# [SNIPPET_DESCRIPTION: Different ways to connect signals and slots]
# [SNIPPET_AUTHOR: Darren Worrall <[email protected]>]
# [SNIPPET_LICENSE: GPL]
# [SNIPPET_DOCS: http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/pyqt4ref.html#connecting-disconnecting-and-emitting-signals]

# example signalsandslots.py

# In this example we will simulate some things that are done automatically
# if you are building your ui's with Qt Designer and the pyuic4 tools

import sys
from PyQt4 import QtGui, QtCore

class SignalsAndSlots(QtGui.QWidget):
    """
    A few signal and slot examples
    """

    def __init__(self):
        # create GUI
        QtGui.QMainWindow.__init__(self)
        self.setWindowTitle('Signals and Slots')
        # Set the window dimensions
        self.resize(450,400)
        
        # vertical layout for widgets
        self.vbox = QtGui.QVBoxLayout()
        self.setLayout(self.vbox)

        # Create a button which we will manually connect to a slot
        self.man_btn = QtGui.QPushButton('Manually connected', self)
        self.vbox.addWidget(self.man_btn)

        # Manually connect the clicked signal to it's handler, as we have done
        # in other examples
        self.connect(self.man_btn, QtCore.SIGNAL('clicked()'), self.manual_slot)
        
        # Create a button which we will automatially connect to a slot
        self.brokenbtn = QtGui.QPushButton("'Broken' Auto Button", self)
        # For autoconnection to work we have to call setobject name. When 
        # using the designer this is done automatially
        self.brokenbtn.setObjectName('brokenbtn')
        self.vbox.addWidget(self.brokenbtn)

        # Create a button which we will automatially connect to a slot, only
        # a bit more carefully :)
        self.workingbtn = QtGui.QPushButton("'Working' Auto Button", self)
        self.workingbtn.setObjectName('workingbtn')
        self.vbox.addWidget(self.workingbtn)

        # Finally a text edit area to show some results
        self.log_window = QtGui.QTextEdit()
        self.vbox.addWidget(self.log_window)
        # read only, sorry folks ;)
        self.log_window.setReadOnly(True)

        # Automatcally connect signals to slots by their name. This is also
        # done for you if you use the result of pyuic4, in the setupUi call
        QtCore.QMetaObject.connectSlotsByName(self)

    def manual_slot(self):
        """
        This slot/handlier is manually connected in __init__
        """
        # A manually connected slot, as pre previous examples. No drama :)
        msg = 'This slot was manually connected, and is called once'
        self.log_window.append(msg)

    def on_brokenbtn_clicked(self):
        """
        This slot is connected automatically in connectSlotsByName
        """
        # connectSlotsByName found this handler - it starts with on_, followed
        # by the object name, followed by the signal this should be connected
        # to. However, as you will see, it is called twice...
        msg = 'This slot was automatically connected, and is called twice'
        self.log_window.append(msg)

    @QtCore.pyqtSlot()     
    def on_workingbtn_clicked(self):
        """
        This slot is connected automatically in connectSlotsByName
        """
        # Using the same logic this slot is connected to the 'working' button
        # and only called once. The reason for this is that a lot of Qt signals
        # are overloaded, and if you dont specify which specific variant you
        # are interested in, then _all_ of them will be connected. Once way to
        # specify the specific signal is by use of the decorator above - in
        # this case, we will be connected to the signal with no arguments
        #
        # It is better explained in the documentation:
        # http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/pyqt4ref.html#connecting-slots-by-name
        msg = 'This slot was automatically connected, and is called once'
        self.log_window.append(msg)



# If the program is run directly or passed as an argument to the python
# interpreter then create a SignalsAndSlots instance and show it
if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    gui = SignalsAndSlots()
    gui.show()
    app.exec_()