Potential Incompatibilities with Earlier Versions — PyQt 4.12 Reference Guide

# Potential Incompatibilities with Earlier Versions

## PyQt4 v4.11

### Execution of Python Slots

Until the release of PyQt4 v4.9.4, when a signal was emitted to a Python slot that was not decorated with `pyqtSlot()`, it would first check that the underlying C++ receiver instance still existed. If it didn’t then the slot was ignored thereby reflecting the standard C++ behaviour.

In v4.9.4 (strictly speaking it was the release of SIP at the time) the check was removed. It was done so that an object could connect its `destroyed()` signal to itself so that it could monitor when its underlying C++ instance was destroyed. Unfortunately this turned out to be an undocumented and incompatible change and a potential source of obscure bugs for more common code.

In v4.11 the check was reintroduced - hence creating an incompatibility for any code that relies on the v4.9.4 behaviour. As a workaround for this the `no_receiver_check` argument has been added to `connect()` which allows the check to be suppressed on a per connection basis.

## PyQt4 v4.12

### qSwap()

The support for `qSwap()` has been removed from all modules. Classes that are supported by this function have a `swap()` method which can be used instead.

## PyQt4 v4.9.2

### QPyNullVariant

In previous versions a `QPyNullVariant` would always evaluate to `True` when converted to a bool.

In this version a `QPyNullVariant` will always evaluate to `False` when converted to a bool. This makes it behave like `None` in these circumstances.

## PyQt4 v4.8.3

### SQL Models

In previous versions, when using v2 of the QVariant API (the default for Python v3), there was no way to represent a null `QVariant`. Therefore when reading values of certain types from an SQL model it was impossible to distinguish between, for example, a null integer and an integer with the value zero.

In this version the `QPyNullVariant` class is used to represent a null QVariant. Therefore the object read from an SQL model may now be a `QPyNullVariant` instance.

A null `QVariant` is only converted to a `QPyNullVariant` if the underlying C++ type of the `QVariant` cannot be tested for null, i.e. it does not implement an `isNull()` method. This ensures that existing code that uses non-SQL models will continue to work unchanged.

## PyQt4 v4.8

### QVariantList

In previous versions PyQt4 would always try and convert a Python list to a `QVariantList`. In this version PyQt4 will first try to convert it to a `QVariant` containing a `QList<QObject *>`, but only if `QList<QObject *>` has been registered with Qt as a meta-type.

Normally it is only the `QtDeclarative` module that registers this meta-type and so the behaviour of existing applications should be unchanged. It is possible however that you might observe different conversion behaviour after importing the `QtDeclarative` module.

## PyQt4 v4.7.4

### `pyqtSignal()` with dict and list

In previous versions a Qt signal defined using `pyqtSignal()` that had an argument specified as a dict then, when emitting a value, PyQt4 would try and convert the value to a `QVariantMap` if possible. If it wasn’t possible, normally because the dict had non-string keys, then the value would be left as a dict object.

In this version PyQt4 will not attempt to convert the value to a `QVariantMap` and will always leave it as a dict object. If you want the value to be converted to a `QVariantMap` then define the signal argument as `'QVariantMap'`.

The same applies to conversions between lists and `QVariantList`.

## PyQt4 v4.7.1

### QVariant

This version introduces a slight incompatibility in the conversion between sub-classes of standard Python types and `QVariant`.

Take, for example, the following code:

    from PyQt4.QtCore import QVariant

    class MyFloat(float):
        pass

    myfloat = MyFloat(5.0)
    variant = QVariant(myfloat)

With this version of PyQt4 `myfloat` will be converted in such a way as to preserve any additional attributes (including methods) and will not be converted to a C++ `double`. In other words, the following assertions are true:

    assert(variant.type() != QVariant.Double)
    assert(variant.toPyObject() is myfloat)

Prior to this version `myfloat` would be converted to a C++ `double`. In other words, the following assertions would be true:

    assert(variant.type() == QVariant.Double)
    assert(variant.toPyObject() == myfloat)
    assert(type(variant.toPyObject()) is float)

The same change also affects objects that implement the sequence protocol. Prior to this version such an object would be converted to a `QVariantList` which would mean that it was converted back to a Python `list` rather than to the original type.

## PyQt4 v4.5

### QVariant

This version introduces a slight incompatibility in the conversion between Python sub-classes of certain Qt classes and `QVariant`. The Qt classes affected are those that `QVariant` has explicit support for, e.g. `QSize`, `QBitmap`.

Take, for example, the following code:

    from PyQt4.QtCore import QSize, QVariant

    class MySize(QSize):
        pass

    mysize = MySize(5, 5)
    variant = QVariant(mysize)

With this version of PyQt4 `mysize` will be converted in such a way as to preserve any additional attributes (including methods) and will not be converted to a C++ `QSize` instance. In other words, the following assertions are true:

    assert(variant.type() != QVariant.Size)
    assert(variant.toPyObject() is mysize)

Prior to this version `mysize` would be converted to a C++ `QSize` instance. In other words, the following assertions would be true:

    assert(variant.type() == QVariant.Size)
    assert(variant.toPyObject() == mysize)
    assert(type(variant.toPyObject()) is QSize)

It is hoped that this change of behaviour will not have a significant impact. However if you need the old behaviour then simply create a copy of your sub-class instance using the base class constructor as shown below:

    variant = QVariant(QSize(mysize))

A similar issue also affects the conversion of the Python `datetime`, `date` and `time` types to `QVariant`. These are no longer converted to the corresponding `QDateTime`, `QDate` and `QTime` classes. The values can be retrieved using `QVariant.toPyObject()`. Again, the old behaviour can be achieved using an explicit conversion to the Qt class before converting to `QVariant`.

A further incompatible change is the handling of Python sub-classes of `QObject`. In previous versions `QVariant.userType()` would return an internal type and an extra reference would be kept to the Python object. 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.

### pyrcc4 Support for Python v3

pyrcc4 will now generate code for Python v3 when the new `-py3` command line option is used. The generated code will also work with Python v2.6 and later.

By default pyrcc4 will generate code for all Python v2 versions but you should use the new `-py2` command line option to enforce this in case the default is changed in the future.