[PyQt] Possible bug with new signal-slot connection
Phil Thompson
phil at riverbankcomputing.com
Wed May 16 21:21:05 BST 2012
On Tue, 15 May 2012 17:34:05 +0300, Alexander Bruy
<alexander.bruy at gmail.com> wrote:
> Hi all,
>
> I try to use new style for signal-slot connection [0] in my project. And
> have one problem that looks like bug in PyQt.
>
> There is a dialog window with QDialogButtonBox, which has two buttons
> OK and Close. During the program execution I need to change behavior
> of Close button: pressing this button should not close the dialog but
> invoke
> another slot. Using old signal-slot connection I implement this as
>
> self.btnClose = self.buttonBox.button( QDialogButtonBox.Close )
> # disconnect from rejected() slot
> QObject.disconnect( self.buttonBox, SIGNAL( "rejected()" ), self.reject
)
> # and connect to user-defined slot
> QObject.connect( self.btnClose, SIGNAL( "clicked()" ),
self.stopProcessing
> )
> ....
> # restore original state
> QObject.disconnect( self.btnClose, SIGNAL( "clicked()" ),
> self.stopProcessing )
> QObject.connect( self.buttonBox, SIGNAL( "rejected()" ), self.reject )
>
> And all works fine as I want. But when i use new style for signal-slot
> connection
> in same code
>
> self.btnClose = self.buttonBox.button( QDialogButtonBox.Close )
> self.buttonBox.rejected.disconnect( self.reject )
> self.btnClose.clicked.connect( self.stopProcessing )
> ...
> self.btnClose.clicked.disconnect( self.stopProcessing )
> self.buttonBox.rejected.connect( self.reject )
>
> I get error in run-time
>
> line 147, in prepareProcess
> self.buttonBox.rejected.disconnect( self.reject )
> TypeError: disconnect() failed between ‘rejected’ and ‘reject’
>
> Dialog designed in QtDesigner and compiled to Python code using
> pyuic4.
>
> After some investigating I found that pyuic4 generates code using
> old-style signal-slot connections
>
> QtCore.QObject.connect(self.buttonBox,
> QtCore.SIGNAL(_fromUtf8("accepted()")), Dialog.accept)
> QtCore.QObject.connect(self.buttonBox,
> QtCore.SIGNAL(_fromUtf8("rejected()")), Dialog.reject)
>
> If I replace this string in generated file with new style connections
>
> self.buttonBox.accepted.connect(Dialog.accept)
> self.buttonBox.rejected.connect(Dialog.reject)
>
> my code again works fine. This was tested under Linux: with
> Python 2.6.6, Qt 4.7.0 and PyQt 4.8.1. And under Windows using
> - Python 2.7.3, PyQt 4.9, Qt 4.8.0
> - Python 3.2.2, PyQt 4.9.1, Qt 4.8.0.
>
> I think that incompatibility between new and old style for signal-slot
> connections is a major bug that can cause many problems when UI
> created in Designer and some signals and slots should be connected
> and disconnected in run-time.
>
> I create simple demo for this bug, find it attached.
>
> [0]
>
http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/new_style_signals_slots.html
>
> Thanks and sorry for my English
This is what's happening...
PyQt allows connections to be made to any Python callable.
With old style connections a proxy slot is created under the covers
whenever a callable is used (as opposed to when SLOT() is used to refer to
a C++ slot). Therefore, in your example, a proxy is always created for the
connection to Dialog.reject even though that refers to the object that
wraps the C++ slot (as there is no Python reimplementation of reject()).
New style connections are a bit smarter in that they treat slot objects
that refer to a wrapped C++ slot and objects that refer to some real Python
code differently - a proxy is only created in the latter case.
A possible workaround might be to add a null implementation of reject() to
the BugDemo class...
def reject(self):
super(BugDemo, self).reject()
...which would force new style connections to create a proxy - and for
disconnect() to assume that a proxy was being used.
So the rule is that if you make a connection old style then you must
disconnect old style as well and the same for new style. You also need to
be aware that pyuic4 uses old style connections.
I accept that this is a bug, but I don't want to fix it. The reason is
that the changes required to the old style support code would be quite
significant and I don't think the risk to the stability of that code is
worth it at this stage of the life of PyQt4, particularly as the workaround
is straightforward. This bug has been present for 3 years and it's the
first time I remember it being reported. Obviously I will update the
documentation.
Phil
More information about the PyQt
mailing list