[PyQt] Freezes and crashes with signal autoconnection

Phil Thompson phil at riverbankcomputing.com
Tue Dec 8 09:26:33 GMT 2009


On Mon, 7 Dec 2009 16:24:19 -0800 (PST), Christian Roche
<christian.roche.fr at gmail.com> wrote:
> However now I have another problem. Following Phill latest advice, I read
> the following part of the documentation:
> 
> 
>> In the current version QVariant.userType() will correctly return
>> QMetaType.QObjectStar (or QMetaType.QWidgetStar) but an extra reference
>> to
>> the Python object is not kept. To avoid a potential crash you should
>> ensure that you keep a separate reference to the Python object, either
>> explicitly or implicitly by giving it a parent.
>> 
> Unfortunaltely these sentences are totally unclear to me. When is
> QVariant.userType() used? Who does or does not keep a reference to the
> Python object, and when? In which situation would a crash potentially
> occur?

When a value is passed between threads Qt wraps it in a QVariant and uses
the event system to send it to the correct thread.

PyQt can convert any Python object to a QVariant. If possible it uses the
standard QVariant types to do so. This means that if the code using the
value is C++ then it has a chance of working. The original Python object
gets discarded, ie. it's reference count is left unchanged. This is fine
because the standard QVariant types (with a couple of exceptions) are value
types (as opposed to pointer types) so they can be safely copied by Qt and
exist independently of the original Python object.

Other Python objects do not correspond to the standard QVariant types, so
PyQt has an internal C++ class that can wrap a Python object and can itself
be wrapped in a QVariant. As a QVariant can be copied around inside Qt the
internal PyQt class manages the reference count of the Python object it is
wrapping so that it stays alive for as long as it is needed, and garbage
collected when it is not.

All the above happens automatically.

The problem is that the QObjectStar and QWidgetStar types are pointer types
and not value types. If the value being pointed to is owned by Python then
it's important that the reference count is maintained, otherwise the
QObject/QWidget will be destroyed when the Python object is garbage
collected and the QVariant will be left with a dangling pointer and will
cause a crash. Ideally these types would be wrapped by the same internal
PyQt class which would manage the reference count. The problem is that any
C++ code that is expecting a QObjectStar/QWidgetStar wouldn't know how to
get it out of the wrapper. The main use case is making Python objects
available as JavaScript objects in QtScript.

Therefore, in these specific cases, if it necessary for your code to ensure
that there is an explicit reference kept to any QObject or QWidget you are
passing between threads.

> Worse, eventhough I don't understand the "why", I tried to implement the
> "how", i.e., naively added the following lines to my code, hoping to have
> given my poor lonesome QObject some parent by doing so, and not any
> ordinary
> parent, the parent of all parents, the very QApplication itself:
> 
> 
>> class ImageLink(QObject):
>> 
>>     def __init__(self, base, url, href):
>>         QObject.__init__(self)
>>         app = QApplication.instance()
>>         self.moveToThread(app.thread())
>>         self.setParent(app)
>>         blah blah
>> 
> Alas, poor Yorich, all this no no avail. Still it crashes faster than the
> lightning, and grieves my sore heart and my tired soul.

I've already suggested that you should simplify your ImageLink class so
that it does not derive from QObject.

Phil


More information about the PyQt mailing list