[PyQt] Proposal for New-style Signals

Phil Thompson phil at riverbankcomputing.com
Mon Jan 21 15:20:16 GMT 2008


This reflects my current thinking on introducing new, more convenient ways of 
using signals in PyQt. The aims are twofold: to make the use of signals less 
error prone and less verbose.

Any changes must preserve backwards compatibility and must provide a general 
solution (not just for Qt but also 3rd party libraries that might not have 
the same level of thought put into their APIs).

A key question is how to specify signatures, ie. using strings (as now), or 
using a sequence of Python type objects, or using some naming convention for 
methods. I intend to retain the use of strings because:

    - they are guaranteed to be flexible enough for future needs
    - Python types aren't specific enough to cover all C++ types
    - to be consistent with __pyqtSignals__, pyqtProperty and pyqtSignature.

I plan to make changes in two stages.


Stage 1

The use of SIGNAL() will become optional for Qt signals and short-circuit 
signals.  SIGNAL() must still be used for Python signals (but Python signals 
should be considered deprecated in favour of short-circuit signals).  Note 
that a signal defined dynamically using __pyqtSignals__ is considered to be a 
Qt signal.

Signals are passed to QObject's [dis]connect() and emit() methods.  Currently, 
If the signal is invalid then no indication is provided to that effect.  If a 
simple string is used instead of SIGNAL() then an invalid Qt signal will 
cause a Python exception to be raised.  As now, an "invalid" short-circuit 
signal will not cause an exception to be raised.

For example:

    QObject.connect(btn, 'clicked()', self.on_clicked)
    QObject.connect(foo, 'zapped', on_zapped)

    btn.emit('clicked()')
    foo.emit('zapped', arg1, arg2)

Question: It would be possible to allow (but not require) short-circuit 
signals to be predefined using __pyqtSignals__.  [dis]connect() and emit() 
could then check the name (but not any arguments) of the signal.  A check 
would only fail if at least one short-circuit signal had been defined (to 
preserve backwards compatibility).  Is this worthwhile?  (My feeling is no.)


Stage 2

The Qt signals with a particular name will be represented by a new signal 
object stored as a class attribute.  Individual signals (ie. with a 
particular signature) will be accessed as an index using a string containing 
a comma separated list of argument types as the key.

Each individual signal will have [dis]connect() and emit() methods.

For example:

    btn.clicked[''].connect(self.on_clicked)
    btn.clicked['bool'].connect(self.on_clicked_checked)

    btn.toggled['bool'].emit(True)

In addition, for signal overloads with no arguments (ie. with '' as the signal 
key) the index can be omitted as follows:

    btn.clicked.connect(self.on_clicked)

Note that for a signal with at least one argument, but without an overload 
that has no arguments, this "short form" is invalid.  This is to allow for 
the addition of a no-argument overload some time in the future.

Question: An alternative approach would be to define a "default" signal (in 
the .sip files) which would identify the overload to be used with the "short 
form".  As most Qt signals aren't overloaded this would mean that the type 
string would not have to be specified in the majority of cases.  Is this 
worthwhile? (My feeling is yes.)

The signal names (but not the types) will be included in the API file for code 
completion support.

Thanks to those who have been throwing their ideas around. Comments welcome.

Phil


More information about the PyQt mailing list