// Serialise operator.
QDataStream &operator<<(QDataStream &out, const PyQt_PyObject &obj)
{
    PyObject *ser_obj = 0;
    const char *ser = 0;
    uint len = 0;

    if (obj.pyobject)
    {
        static PyObject *dumps = 0;

        SIP_BLOCK_THREADS

        if (!dumps)
        {
            PyObject *pickle = PyImport_ImportModule("pickle");

            if (pickle)
            {
                dumps = PyObject_GetAttrString(pickle, "dumps");
                Py_DECREF(pickle);
            }
        }

        if (dumps)
        {
            ser_obj = PyObject_CallFunctionObjArgs(dumps, obj.pyobject, 0);

            if (ser_obj)
            {
                if (SIPBytes_Check(ser_obj))
                {
                    ser = SIPBytes_AS_STRING(ser_obj);
                    len = SIPBytes_GET_SIZE(ser_obj);
                }
                else
                {
                    Py_DECREF(ser_obj);
                    ser_obj = 0;
                }
            }
            else
            {
                PyErr_Print();
            }
        }

        SIP_UNBLOCK_THREADS
    }

    out.writeBytes(ser, len);

    if (ser_obj)
    {
        SIP_BLOCK_THREADS
        Py_DECREF(ser_obj);
        SIP_UNBLOCK_THREADS
    }

    return out;
}
Exemple #2
0
// Create a dynamic meta-object for a Python type by introspecting its
// attributes.  Note that it leaks if the type is deleted.
static int create_dynamic_metaobject(pyqtWrapperType *pyqt_wt)
{
    PyTypeObject *pytype = (PyTypeObject *)pyqt_wt;
    qpycore_metaobject *qo = new qpycore_metaobject;

    // Get the super-type's meta-object.
    qo->mo.d.superdata = get_qmetaobject((pyqtWrapperType *)pytype->tp_base);

    // Get the name of the type.  Dynamic types have simple names.
    qo->str_data = pytype->tp_name;
    qo->str_data.append('\0');

    // Go through the class dictionary getting all PyQt properties, slots,
    // signals or a (deprecated) sequence of signals.

    typedef QPair<PyObject *, PyObject *> prop_data;
    QMap<uint, prop_data> pprops;
    QList<QByteArray> psigs;
    QList<const QMetaObject *> enum_scopes;
    SIP_SSIZE_T pos = 0;
    PyObject *key, *value;

    while (PyDict_Next(pytype->tp_dict, &pos, &key, &value))
    {
        // See if it is a slot, ie. it has been decorated with pyqtSlot().
        PyObject *sig_obj = PyObject_GetAttr(value,
                qpycore_signature_attr_name);

        if (sig_obj)
        {
            // Make sure it is a list and not some legitimate attribute that
            // happens to use our special name.
            if (PyList_Check(sig_obj))
            {
                for (SIP_SSIZE_T i = 0; i < PyList_GET_SIZE(sig_obj); ++i)
                {
                    qpycore_slot slot;

                    // Set up the skeleton slot.
                    PyObject *decoration = PyList_GET_ITEM(sig_obj, i);
                    slot.signature = Chimera::Signature::fromPyObject(decoration);

                    slot.sip_slot.pyobj = 0;
                    slot.sip_slot.name = 0;
                    slot.sip_slot.meth.mfunc = value;
                    slot.sip_slot.meth.mself = 0;
#if PY_MAJOR_VERSION < 3
                    slot.sip_slot.meth.mclass = (PyObject *)pyqt_wt;
#endif
                    slot.sip_slot.weakSlot = 0;

                    qo->pslots.append(slot);
                }
            }

            Py_DECREF(sig_obj);
        }
        else
        {
            PyErr_Clear();

            // Make sure the key is an ASCII string.  Delay the error checking
            // until we know we actually need it.
            const char *ascii_key = sipString_AsASCIIString(&key);

            // See if the value is of interest.
            if (PyType_IsSubtype(Py_TYPE(value), &qpycore_pyqtProperty_Type))
            {
                // It is a property.

                if (!ascii_key)
                    return -1;

                Py_INCREF(value);

                qpycore_pyqtProperty *pp = (qpycore_pyqtProperty *)value;

                pprops.insert(pp->pyqtprop_sequence, prop_data(key, value));

                // See if the property has a scope.  If so, collect all
                // QMetaObject pointers that are not in the super-class
                // hierarchy.
                const QMetaObject *mo = get_scope_qmetaobject(pp->pyqtprop_parsed_type);

                if (mo && !enum_scopes.contains(mo))
                    enum_scopes.append(mo);
            }
            else if (PyType_IsSubtype(Py_TYPE(value), &qpycore_pyqtSignal_Type))
            {
                // It is a signal.

                if (!ascii_key)
                    return -1;

                qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)value;

                // Make sure the signal has a name.
                qpycore_set_signal_name(ps, ascii_key);

                // Add all the overloads.
                for (int ol = 0; ol < ps->overloads->size(); ++ol)
                    psigs.append(ps->overloads->at(ol)->signature);

                Py_DECREF(key);
            }
            else if (ascii_key && qstrcmp(ascii_key, "__pyqtSignals__") == 0)
            {
                // It is a sequence of signal signatures.  This is deprecated
                // in favour of pyqtSignal().

                if (PySequence_Check(value))
                {
                    SIP_SSIZE_T seq_sz = PySequence_Size(value);

                    for (SIP_SSIZE_T i = 0; i < seq_sz; ++i)
                    {
                        PyObject *itm = PySequence_ITEM(value, i);

                        if (!itm)
                        {
                            Py_DECREF(key);
                            return -1;
                        }

                        PyObject *ascii_itm = itm;
                        const char *ascii = sipString_AsASCIIString(&ascii_itm);
                        Py_DECREF(itm);

                        if (!ascii)
                        {
                            Py_DECREF(key);
                            return -1;
                        }

                        QByteArray old_sig = QMetaObject::normalizedSignature(ascii);
                        old_sig.prepend('2');
                        psigs.append(old_sig);

                        Py_DECREF(ascii_itm);
                    }
                }

                Py_DECREF(key);
            }
        }
    }

    qo->nr_signals = psigs.count();

    // Create and fill the extradata array.
    if (enum_scopes.isEmpty())
    {
        qo->mo.d.extradata = 0;
    }
    else
    {
        const QMetaObject **objects = new const QMetaObject *[enum_scopes.size() + 1];

        for (int i = 0; i < enum_scopes.size(); ++i)
            objects[i] = enum_scopes.at(i);

        objects[enum_scopes.size()] = 0;

        qo->mo.d.extradata = objects;
    }

    // Initialise the header section of the data table.  Qt v4.5 introduced
    // revision 2 which added constructors.  However the design is broken in
    // that the static meta-call function doesn't provide enough information to
    // determine which Python sub-class of a Qt class is to be created.  So we
    // stick with revision 1 (and don't allow pyqtSlot() to decorate __init__).

    const int revision = 1;
    const int header_size = 10;

    int data_len = header_size + qo->nr_signals * 5 + qo->pslots.count() * 5 + 
            pprops.count() * 3 + 1;
    uint *data = new uint[data_len];
    int g_offset = header_size;
    int s_offset = g_offset + qo->nr_signals * 5;
    int p_offset = s_offset + qo->pslots.count() * 5;
    int empty = 0;

    for (int i = 0; i < data_len; ++i)
        data[i] = 0;

    // The revision number.
    data[0] = revision;

    // Set up the methods count and offset.
    if (qo->nr_signals || qo->pslots.count())
    {
        data[4] = qo->nr_signals + qo->pslots.count();
        data[5] = g_offset;

        // We might need an empty string.
        empty = qo->str_data.size();
        qo->str_data.append('\0');
    }

    // Set up the properties count and offset.
    if (pprops.count())
    {
        data[6] = pprops.count();
        data[7] = p_offset;
    }

    // Add the signals to the meta-object.
    for (int g = 0; g < qo->nr_signals; ++g)
    {
        const QByteArray &norm = psigs.at(g);

        // Add the (non-existent) argument names.
        data[g_offset + (g * 5) + 1] = add_arg_names(qo, norm, empty);

        // Add the full signature.
        data[g_offset + (g * 5) + 0] = qo->str_data.size();
        qo->str_data.append(norm.constData() + 1);
        qo->str_data.append('\0');

        // Add the type, tag and flags.
        data[g_offset + (g * 5) + 2] = empty;
        data[g_offset + (g * 5) + 3] = empty;
        data[g_offset + (g * 5) + 4] = 0x05;
    }

    // Add the slots to the meta-object.
    for (int s = 0; s < qo->pslots.count(); ++s)
    {
        const qpycore_slot &slot = qo->pslots.at(s);
        const QByteArray &sig = slot.signature->signature;

        // Add the (non-existent) argument names.
        data[s_offset + (s * 5) + 1] = add_arg_names(qo, sig, empty);

        // Add the full signature.
        data[s_offset + (s * 5) + 0] = qo->str_data.size();
        qo->str_data.append(sig);
        qo->str_data.append('\0');

        // Add any type.
        if (slot.signature->result)
        {
            data[s_offset + (s * 5) + 2] = qo->str_data.size();
            qo->str_data.append(slot.signature->result->name());
            qo->str_data.append('\0');
        }
        else
        {
            data[s_offset + (s * 5) + 2] = empty;
        }

        // Add the tag and flags.
        data[s_offset + (s * 5) + 3] = empty;
        data[s_offset + (s * 5) + 4] = 0x0a;
    }

    // Add the properties to the meta-object.
    QMapIterator<uint, prop_data> it(pprops);

    for (int p = 0; it.hasNext(); ++p)
    {
        it.next();

        const prop_data &pprop = it.value();
        const char *prop_name = SIPBytes_AS_STRING(pprop.first);
        qpycore_pyqtProperty *pp = (qpycore_pyqtProperty *)pprop.second;

        // Add the property name.
        data[p_offset + (p * 3) + 0] = qo->str_data.size();
        qo->str_data.append(prop_name);
        qo->str_data.append('\0');

        // Add the name of the property type.
        data[p_offset + (p * 3) + 1] = qo->str_data.size();
        qo->str_data.append(pp->pyqtprop_parsed_type->name());
        qo->str_data.append('\0');

        // Add the property flags.
        uint flags = pp->pyqtprop_flags;

        // Enum or flag.
        if (pp->pyqtprop_parsed_type->isEnum() || pp->pyqtprop_parsed_type->isFlag())
            flags |= 0x00000008;

        if (pp->prop_get && PyCallable_Check(pp->prop_get))
            // Readable.
            flags |= 0x00000001;

        if (pp->prop_set && PyCallable_Check(pp->prop_set))
        {
            // Writable.
            flags |= 0x00000002;

            // See if the name of the setter follows the Designer convention.
            // If so tell the UI compilers not to use setProperty().
            PyObject *setter_name_obj = PyObject_GetAttr(pp->prop_set,
                    qpycore_name_attr_name);

            if (setter_name_obj)
            {
                PyObject *ascii_obj = setter_name_obj;
                const char *ascii = sipString_AsASCIIString(&ascii_obj);
                Py_DECREF(setter_name_obj);

                if (ascii)
                {
                    if (qstrlen(ascii) > 3 && ascii[0] == 's' &&
                            ascii[1] == 'e' && ascii[2] == 't' &&
                            ascii[3] == toupper(prop_name[0]) &&
                            qstrcmp(&ascii[4], &prop_name[1]) == 0)
                        flags |= 0x00000100;
                }

                Py_DECREF(ascii_obj);
            }

            PyErr_Clear();
        }

        if (pp->pyqtprop_reset && PyCallable_Check(pp->pyqtprop_reset))
            // Resetable.
            flags |= 0x00000004;

        // There are only 8 bits available for the type so use the special
        // value if more are required.
        uint metatype = pp->pyqtprop_parsed_type->metatype();

        if (metatype > 0xff)
            metatype = 0xff;

        flags |= metatype << 24;

        data[p_offset + (p * 3) + 2] = flags;

        // Save the property data for qt_metacall().  (We already have a
        // reference.)
        qo->pprops.append(pp);

        // We've finished with the property name.
        Py_DECREF(pprop.first);
    }

    // Initialise the rest of the meta-object.
    qo->mo.d.stringdata = qo->str_data.constData();
    qo->mo.d.data = data;

    // Save the meta-object.
    pyqt_wt->metaobject = qo;

    return 0;
}
Exemple #3
0
// Create a dynamic meta-object for a Python type by introspecting its
// attributes.  Note that it leaks if the type is deleted.
static int create_dynamic_metaobject(pyqtWrapperType *pyqt_wt)
{
    PyTypeObject *pytype = (PyTypeObject *)pyqt_wt;
    qpycore_metaobject *qo = new qpycore_metaobject;
#if QT_VERSION >= 0x050000
    QMetaObjectBuilder builder;
#endif

    // Get any class info.
    QList<ClassInfo> class_info_list = qpycore_get_class_info_list();

    // Get the super-type's meta-object.
#if QT_VERSION >= 0x050000
    builder.setSuperClass(get_qmetaobject((pyqtWrapperType *)pytype->tp_base));
#else
    qo->mo.d.superdata = get_qmetaobject((pyqtWrapperType *)pytype->tp_base);
#endif

    // Get the name of the type.  Dynamic types have simple names.
#if QT_VERSION >= 0x050000
    builder.setClassName(pytype->tp_name);
#else
    qo->str_data = pytype->tp_name;
    qo->str_data.append('\0');
#endif

    // Go through the class dictionary getting all PyQt properties, slots,
    // signals or a (deprecated) sequence of signals.

    typedef QPair<PyObject *, PyObject *> prop_data;
    QMap<uint, prop_data> pprops;
    QList<QByteArray> psigs;
    SIP_SSIZE_T pos = 0;
    PyObject *key, *value;
#if QT_VERSION < 0x050000
    bool has_notify_signal = false;
    QList<const QMetaObject *> enum_scopes;
#endif

    while (PyDict_Next(pytype->tp_dict, &pos, &key, &value))
    {
        // See if it is a slot, ie. it has been decorated with pyqtSlot().
        PyObject *sig_obj = PyObject_GetAttr(value,
                qpycore_signature_attr_name);

        if (sig_obj)
        {
            // Make sure it is a list and not some legitimate attribute that
            // happens to use our special name.
            if (PyList_Check(sig_obj))
            {
                for (SIP_SSIZE_T i = 0; i < PyList_GET_SIZE(sig_obj); ++i)
                {
                    qpycore_slot slot;

                    // Set up the skeleton slot.
                    PyObject *decoration = PyList_GET_ITEM(sig_obj, i);
                    slot.signature = Chimera::Signature::fromPyObject(decoration);

                    slot.sip_slot.pyobj = 0;
                    slot.sip_slot.name = 0;
                    slot.sip_slot.meth.mfunc = value;
                    slot.sip_slot.meth.mself = 0;
#if PY_MAJOR_VERSION < 3
                    slot.sip_slot.meth.mclass = (PyObject *)pyqt_wt;
#endif
                    slot.sip_slot.weakSlot = 0;

                    qo->pslots.append(slot);
                }
            }

            Py_DECREF(sig_obj);
        }
        else
        {
            PyErr_Clear();

            // Make sure the key is an ASCII string.  Delay the error checking
            // until we know we actually need it.
            const char *ascii_key = sipString_AsASCIIString(&key);

            // See if the value is of interest.
            if (PyType_IsSubtype(Py_TYPE(value), &qpycore_pyqtProperty_Type))
            {
                // It is a property.

                if (!ascii_key)
                    return -1;

                Py_INCREF(value);

                qpycore_pyqtProperty *pp = (qpycore_pyqtProperty *)value;

                pprops.insert(pp->pyqtprop_sequence, prop_data(key, value));

                // See if the property has a scope.  If so, collect all
                // QMetaObject pointers that are not in the super-class
                // hierarchy.
                const QMetaObject *mo = get_scope_qmetaobject(pp->pyqtprop_parsed_type);

#if QT_VERSION >= 0x050000
                if (mo)
                    builder.addRelatedMetaObject(mo);
#else
                if (mo && !enum_scopes.contains(mo))
                    enum_scopes.append(mo);
#endif

#if QT_VERSION < 0x050000
                // See if the property has a notify signal so that we can
                // allocate space for it in the meta-object.  We will check if
                // it is valid later on.  If there is one property with a
                // notify signal then a signal index is stored for all
                // properties.
                if (pp->pyqtprop_notify)
                    has_notify_signal = true;
#endif
            }
            else if (PyType_IsSubtype(Py_TYPE(value), &qpycore_pyqtSignal_Type))
            {
                // It is a signal.

                if (!ascii_key)
                    return -1;

                qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)value;

                // Make sure the signal has a name.
                qpycore_set_signal_name(ps, pytype->tp_name, ascii_key);

                // Add all the overloads.
                do
                {
                    psigs.append(ps->signature->signature);

                    ps = ps->next;
                }
                while (ps);

                Py_DECREF(key);
            }
            else if (ascii_key && qstrcmp(ascii_key, "__pyqtSignals__") == 0)
            {
                // It is a sequence of signal signatures.  This is deprecated
                // in favour of pyqtSignal().

                if (PySequence_Check(value))
                {
                    SIP_SSIZE_T seq_sz = PySequence_Size(value);

                    for (SIP_SSIZE_T i = 0; i < seq_sz; ++i)
                    {
                        PyObject *itm = PySequence_ITEM(value, i);

                        if (!itm)
                        {
                            Py_DECREF(key);
                            return -1;
                        }

                        PyObject *ascii_itm = itm;
                        const char *ascii = sipString_AsASCIIString(&ascii_itm);
                        Py_DECREF(itm);

                        if (!ascii)
                        {
                            Py_DECREF(key);
                            return -1;
                        }

                        QByteArray old_sig = QMetaObject::normalizedSignature(ascii);
                        old_sig.prepend('2');
                        psigs.append(old_sig);

                        Py_DECREF(ascii_itm);
                    }
                }

                Py_DECREF(key);
            }
            else
            {
                PyErr_Clear();
            }
        }
    }

    qo->nr_signals = psigs.count();

#if QT_VERSION < 0x050000
    // Create and fill the extradata array.
    if (enum_scopes.isEmpty())
    {
        qo->mo.d.extradata = 0;
    }
    else
    {
        const QMetaObject **objects = new const QMetaObject *[enum_scopes.size() + 1];

        for (int i = 0; i < enum_scopes.size(); ++i)
            objects[i] = enum_scopes.at(i);

        objects[enum_scopes.size()] = 0;

#if QT_VERSION >= 0x040600
        qo->ed.objects = objects;
        qo->ed.static_metacall = 0;

        qo->mo.d.extradata = &qo->ed;
#else
        qo->mo.d.extradata = objects;
#endif
    }
#endif

    // Initialise the header section of the data table.  Note that Qt v4.5
    // introduced revision 2 which added constructors.  However the design is
    // broken in that the static meta-call function doesn't provide enough
    // information to determine which Python sub-class of a Qt class is to be
    // created.  So we stick with revision 1 (and don't allow pyqtSlot() to
    // decorate __init__).

#if QT_VERSION < 0x050000
#if QT_VERSION >= 0x040600
    const int revision = 4;
    const int header_size = 14;
#else
    const int revision = 1;
    const int header_size = 10;
#endif

    int data_len = header_size + class_info_list.count() * 2 +
            qo->nr_signals * 5 + qo->pslots.count() * 5 + 
            pprops.count() * (has_notify_signal ? 4 : 3) + 1;
    uint *data = new uint[data_len];
    int i_offset = header_size;
    int g_offset = i_offset + class_info_list.count() * 2;
    int s_offset = g_offset + qo->nr_signals * 5;
    int p_offset = s_offset + qo->pslots.count() * 5;
    int n_offset = p_offset + pprops.count() * 3;
    int empty = 0;

    for (int i = 0; i < data_len; ++i)
        data[i] = 0;

    // The revision number.
    data[0] = revision;
#endif

    // Set up any class information.
    if (class_info_list.count() > 0)
    {
#if QT_VERSION < 0x050000
        data[2] = class_info_list.count();
        data[3] = i_offset;
#endif

        for (int i = 0; i < class_info_list.count(); ++i)
        {
            const ClassInfo &ci = class_info_list.at(i);

#if QT_VERSION >= 0x050000
            builder.addClassInfo(ci.first, ci.second);
#else
            data[i_offset + (i * 2) + 0] = qo->str_data.size();
            qo->str_data.append(ci.first.constData());
            qo->str_data.append('\0');

            data[i_offset + (i * 2) + 1] = qo->str_data.size();
            qo->str_data.append(ci.second.constData());
            qo->str_data.append('\0');
#endif
        }
    }

#if QT_VERSION < 0x050000
    // Set up the methods count and offset.
    if (qo->nr_signals || qo->pslots.count())
    {
        data[4] = qo->nr_signals + qo->pslots.count();
        data[5] = g_offset;

        // We might need an empty string.
        empty = qo->str_data.size();
        qo->str_data.append('\0');
    }

    // Set up the properties count and offset.
    if (pprops.count())
    {
        data[6] = pprops.count();
        data[7] = p_offset;
    }

#if QT_VERSION >= 0x040600
    data[13] = qo->nr_signals;
#endif
#endif

    // Add the signals to the meta-object.
    for (int g = 0; g < qo->nr_signals; ++g)
    {
        const QByteArray &norm = psigs.at(g);

#if QT_VERSION >= 0x050000
        builder.addSignal(norm.mid(1));
#else
        // Add the (non-existent) argument names.
        data[g_offset + (g * 5) + 1] = add_arg_names(qo, norm, empty);

        // Add the full signature.
        data[g_offset + (g * 5) + 0] = qo->str_data.size();
        qo->str_data.append(norm.constData() + 1);
        qo->str_data.append('\0');

        // Add the type, tag and flags.
        data[g_offset + (g * 5) + 2] = empty;
        data[g_offset + (g * 5) + 3] = empty;
        data[g_offset + (g * 5) + 4] = 0x05;
#endif
    }

    // Add the slots to the meta-object.
    for (int s = 0; s < qo->pslots.count(); ++s)
    {
        const qpycore_slot &slot = qo->pslots.at(s);
        const QByteArray &sig = slot.signature->signature;

#if QT_VERSION >= 0x050000
        QMetaMethodBuilder slot_builder = builder.addSlot(sig);
#else
        // Add the (non-existent) argument names.
        data[s_offset + (s * 5) + 1] = add_arg_names(qo, sig, empty);

        // Add the full signature.
        data[s_offset + (s * 5) + 0] = qo->str_data.size();
        qo->str_data.append(sig);
        qo->str_data.append('\0');
#endif

        // Add any type.
        if (slot.signature->result)
        {
#if QT_VERSION >= 0x050000
            slot_builder.setReturnType(slot.signature->result->name());
#else
            data[s_offset + (s * 5) + 2] = qo->str_data.size();
            qo->str_data.append(slot.signature->result->name());
            qo->str_data.append('\0');
#endif
        }
#if QT_VERSION < 0x050000
        else
        {
            data[s_offset + (s * 5) + 2] = empty;
        }

        // Add the tag and flags.
        data[s_offset + (s * 5) + 3] = empty;
        data[s_offset + (s * 5) + 4] = 0x0a;
#endif
    }

    // Add the properties to the meta-object.
#if QT_VERSION < 0x050000
    QList<uint> notify_signals;
#endif
    QMapIterator<uint, prop_data> it(pprops);

    for (int p = 0; it.hasNext(); ++p)
    {
        it.next();

        const prop_data &pprop = it.value();
        const char *prop_name = SIPBytes_AS_STRING(pprop.first);
        qpycore_pyqtProperty *pp = (qpycore_pyqtProperty *)pprop.second;
        int notifier_id;
#if QT_VERSION < 0x050000
        uint flags = 0;
#endif

        if (pp->pyqtprop_notify)
        {
            qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)pp->pyqtprop_notify;
            const QByteArray &sig = ps->signature->signature;

#if QT_VERSION >= 0x050000
            notifier_id = builder.indexOfSignal(sig.mid(1));
#else
            notifier_id = psigs.indexOf(sig);
#endif

            if (notifier_id < 0)
            {
                PyErr_Format(PyExc_TypeError,
                        "the notify signal '%s' was not defined in this class",
                        sig.constData() + 1);

                // Note that we leak the property name.
                return -1;
            }

#if QT_VERSION < 0x050000
            notify_signals.append(notifier_id);
            flags |= 0x00400000;
#endif
        }
        else
        {
#if QT_VERSION >= 0x050000
            notifier_id = -1;
#else
            notify_signals.append(0);
#endif
        }

#if QT_VERSION >= 0x050000
        // A Qt v5 revision 7 meta-object holds the QMetaType::Type of the type
        // or its name if it is unresolved (ie. not known to the type system).
        // In Qt v4 both are held.  For QObject sub-classes Chimera will fall
        // back to the QMetaType::QObjectStar if there is no specific meta-type
        // for the sub-class.  This means that, for Qt v4,
        // QMetaProperty::read() can handle the type.  However, Qt v5 doesn't
        // know that the unresolved type is a QObject sub-class.  Therefore we
        // have to tell it that the property is a QObject, rather than the
        // sub-class.  This means that QMetaProperty.typeName() will always
        // return "QObject*".
        QByteArray prop_type;

        if (pp->pyqtprop_parsed_type->metatype() == QMetaType::QObjectStar)
            prop_type = "QObject*";
        else
            prop_type = pp->pyqtprop_parsed_type->name();

        QMetaPropertyBuilder prop_builder = builder.addProperty(prop_name,
                prop_type, notifier_id);

        // Reset the defaults.
        prop_builder.setReadable(false);
        prop_builder.setWritable(false);
#else
        // Add the property name.
        data[p_offset + (p * 3) + 0] = qo->str_data.size();
        qo->str_data.append(prop_name);
        qo->str_data.append('\0');

        // Add the name of the property type.
        data[p_offset + (p * 3) + 1] = qo->str_data.size();
        qo->str_data.append(pp->pyqtprop_parsed_type->name());
        qo->str_data.append('\0');

        // There are only 8 bits available for the type so use the special
        // value if more are required.
        uint metatype = pp->pyqtprop_parsed_type->metatype();

        if (metatype > 0xff)
        {
#if QT_VERSION >= 0x040600
            // Qt assumes it is an enum.
            metatype = 0;
            flags |= 0x00000008;
#else
            // This is the old PyQt behaviour.  It may not be correct, but
            // nobody has complained, and if it ain't broke...
            metatype = 0xff;
#endif
        }
#endif

        // Enum or flag.
        if (pp->pyqtprop_parsed_type->isEnum() || pp->pyqtprop_parsed_type->isFlag())
        {
#if QT_VERSION >= 0x050000
            prop_builder.setEnumOrFlag(true);
#else
#if QT_VERSION >= 0x040600
            metatype = 0;
#endif
            flags |= 0x00000008;
#endif
        }

#if QT_VERSION < 0x050000
        flags |= metatype << 24;
#endif

        if (pp->pyqtprop_get && PyCallable_Check(pp->pyqtprop_get))
            // Readable.
#if QT_VERSION >= 0x050000
            prop_builder.setReadable(true);
#else
            flags |= 0x00000001;
#endif

        if (pp->pyqtprop_set && PyCallable_Check(pp->pyqtprop_set))
        {
            // Writable.
#if QT_VERSION >= 0x050000
            prop_builder.setWritable(true);
#else
            flags |= 0x00000002;
#endif

            // See if the name of the setter follows the Designer convention.
            // If so tell the UI compilers not to use setProperty().
            PyObject *setter_name_obj = PyObject_GetAttr(pp->pyqtprop_set,
                    qpycore_name_attr_name);

            if (setter_name_obj)
            {
                PyObject *ascii_obj = setter_name_obj;
                const char *ascii = sipString_AsASCIIString(&ascii_obj);
                Py_DECREF(setter_name_obj);

                if (ascii)
                {
                    if (qstrlen(ascii) > 3 && ascii[0] == 's' &&
                            ascii[1] == 'e' && ascii[2] == 't' &&
                            ascii[3] == toupper(prop_name[0]) &&
                            qstrcmp(&ascii[4], &prop_name[1]) == 0)
#if QT_VERSION >= 0x050000
                        prop_builder.setStdCppSet(true);
#else
                        flags |= 0x00000100;
#endif
                }

                Py_DECREF(ascii_obj);
            }

            PyErr_Clear();
        }

        if (pp->pyqtprop_reset && PyCallable_Check(pp->pyqtprop_reset))
            // Resetable.
#if QT_VERSION >= 0x050000
            prop_builder.setResettable(true);
#else
            flags |= 0x00000004;
#endif

        // Add the property flags.
#if QT_VERSION >= 0x050000
        // Note that Qt4 always seems to have ResolveEditable set but
        // QMetaObjectBuilder doesn't provide an API call to do it.
        prop_builder.setDesignable(pp->pyqtprop_flags & 0x00001000);
        prop_builder.setScriptable(pp->pyqtprop_flags & 0x00004000);
        prop_builder.setStored(pp->pyqtprop_flags & 0x00010000);
        prop_builder.setUser(pp->pyqtprop_flags & 0x00100000);
        prop_builder.setConstant(pp->pyqtprop_flags & 0x00000400);
        prop_builder.setFinal(pp->pyqtprop_flags & 0x00000800);
#else
        flags |= pp->pyqtprop_flags;

        data[p_offset + (p * 3) + 2] = flags;
#endif

        // Save the property data for qt_metacall().  (We already have a
        // reference.)
        qo->pprops.append(pp);

        // We've finished with the property name.
        Py_DECREF(pprop.first);
    }

#if QT_VERSION < 0x050000
    // Add the indices of the notify signals.
    if (has_notify_signal)
    {
        QListIterator<uint> notify_it(notify_signals);

        while (notify_it.hasNext())
            data[n_offset++] = notify_it.next();
    }
#endif

    // Initialise the rest of the meta-object.
#if QT_VERSION >= 0x050000
    qo->mo = builder.toMetaObject();
#else
    qo->mo.d.stringdata = qo->str_data.constData();
    qo->mo.d.data = data;
#endif

    // Save the meta-object.
    pyqt_wt->metaobject = qo;

    return 0;
}
Exemple #4
0
// Create a dynamic meta-object for a Python type by introspecting its
// attributes.  Note that it leaks if the type is deleted.
static int create_dynamic_metaobject(pyqtWrapperType *pyqt_wt)
{
    PyTypeObject *pytype = (PyTypeObject *)pyqt_wt;
    qpycore_metaobject *qo = new qpycore_metaobject;
    QMetaObjectBuilder builder;

    // Get any class info.
    QList<ClassInfo> class_info_list = qpycore_get_class_info_list();

    // Get any enums/flags.
    QList<EnumsFlags> enums_flags_list = qpycore_get_enums_flags_list();

    // Get the super-type's meta-object.
    builder.setSuperClass(get_qmetaobject((pyqtWrapperType *)pytype->tp_base));

    // Get the name of the type.  Dynamic types have simple names.
    builder.setClassName(pytype->tp_name);

    // Go through the class hierarchy getting all PyQt properties, slots and
    // signals.

    QList<const qpycore_pyqtSignal *> psigs;
    QMap<uint, PropertyData> pprops;

    if (trawl_hierarchy(pytype, qo, builder, psigs, pprops) < 0)
        return -1;

    qo->nr_signals = psigs.count();

    // Initialise the header section of the data table.  Note that Qt v4.5
    // introduced revision 2 which added constructors.  However the design is
    // broken in that the static meta-call function doesn't provide enough
    // information to determine which Python sub-class of a Qt class is to be
    // created.  So we stick with revision 1 (and don't allow pyqtSlot() to
    // decorate __init__).

    // Set up any class information.
    for (int i = 0; i < class_info_list.count(); ++i)
    {
        const ClassInfo &ci = class_info_list.at(i);

        builder.addClassInfo(ci.first, ci.second);
    }

    // Set up any enums/flags.
    for (int i = 0; i < enums_flags_list.count(); ++i)
    {
        const EnumsFlags &ef = enums_flags_list.at(i);

        QByteArray scoped_name(pytype->tp_name);
        scoped_name.append("::");
        scoped_name.append(ef.name);
        QMetaEnumBuilder enum_builder = builder.addEnumerator(scoped_name);

        enum_builder.setIsFlag(ef.isFlag);

        QHash<QByteArray, int>::const_iterator it = ef.keys.constBegin();

        while (it != ef.keys.constEnd())
        {
            enum_builder.addKey(it.key(), it.value());
            ++it;
        }
    }

    // Add the signals to the meta-object.
    for (int g = 0; g < qo->nr_signals; ++g)
    {
        const qpycore_pyqtSignal *ps = psigs.at(g);

        QMetaMethodBuilder signal_builder = builder.addSignal(
                ps->parsed_signature->signature.mid(1));

        if (ps->parameter_names)
            signal_builder.setParameterNames(*ps->parameter_names);

        signal_builder.setRevision(ps->revision);
    }

    // Add the slots to the meta-object.
    for (int s = 0; s < qo->pslots.count(); ++s)
    {
        const Chimera::Signature *slot_signature = qo->pslots.at(s)->slotSignature();
        const QByteArray &sig = slot_signature->signature;

        QMetaMethodBuilder slot_builder = builder.addSlot(sig);

        // Add any type.
        if (slot_signature->result)
            slot_builder.setReturnType(slot_signature->result->name());

        slot_builder.setRevision(slot_signature->revision);
    }

    // Add the properties to the meta-object.
    QMapIterator<uint, PropertyData> it(pprops);

    for (int p = 0; it.hasNext(); ++p)
    {
        it.next();

        const PropertyData &pprop = it.value();
        const char *prop_name = SIPBytes_AS_STRING(pprop.first);
        qpycore_pyqtProperty *pp = (qpycore_pyqtProperty *)pprop.second;
        int notifier_id;

        if (pp->pyqtprop_notify)
        {
            qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)pp->pyqtprop_notify;
            const QByteArray &sig = ps->parsed_signature->signature;

            notifier_id = builder.indexOfSignal(sig.mid(1));

            if (notifier_id < 0)
            {
                PyErr_Format(PyExc_TypeError,
                        "the notify signal '%s' was not defined in this class",
                        sig.constData() + 1);

                // Note that we leak the property name.
                return -1;
            }
        }
        else
        {
            notifier_id = -1;
        }

        // A Qt v5 revision 7 meta-object holds the QMetaType::Type of the type
        // or its name if it is unresolved (ie. not known to the type system).
        // In Qt v4 both are held.  For QObject sub-classes Chimera will fall
        // back to the QMetaType::QObjectStar if there is no specific meta-type
        // for the sub-class.  This means that, for Qt v4,
        // QMetaProperty::read() can handle the type.  However, Qt v5 doesn't
        // know that the unresolved type is a QObject sub-class.  Therefore we
        // have to tell it that the property is a QObject, rather than the
        // sub-class.  This means that QMetaProperty.typeName() will always
        // return "QObject*".
        QByteArray prop_type;

        if (pp->pyqtprop_parsed_type->metatype() == QMetaType::QObjectStar)
        {
            // However, if the type is a Python sub-class of QObject then we
            // use the name of the Python type.  This anticipates that the type
            // is one that will be proxied by QML at some point.
            if (pp->pyqtprop_parsed_type->typeDef() == sipType_QObject)
            {
                prop_type = ((PyTypeObject *)pp->pyqtprop_parsed_type->py_type())->tp_name;
                prop_type.append('*');
            }
            else
            {
                prop_type = "QObject*";
            }
        }
        else
        {
            prop_type = pp->pyqtprop_parsed_type->name();
        }

        QMetaPropertyBuilder prop_builder = builder.addProperty(prop_name,
                prop_type, notifier_id);

        // Reset the defaults.
        prop_builder.setReadable(false);
        prop_builder.setWritable(false);

        // Enum or flag.
        if (pp->pyqtprop_parsed_type->isEnum() || pp->pyqtprop_parsed_type->isFlag())
        {
            prop_builder.setEnumOrFlag(true);
        }

        if (pp->pyqtprop_get && PyCallable_Check(pp->pyqtprop_get))
        {
            // Readable.
            prop_builder.setReadable(true);
        }

        if (pp->pyqtprop_set && PyCallable_Check(pp->pyqtprop_set))
        {
            // Writable.
            prop_builder.setWritable(true);

            // See if the name of the setter follows the Designer convention.
            // If so tell the UI compilers not to use setProperty().
            PyObject *setter_name_obj = PyObject_GetAttr(pp->pyqtprop_set,
                    qpycore_dunder_name);

            if (setter_name_obj)
            {
                PyObject *ascii_obj = setter_name_obj;
                const char *ascii = sipString_AsASCIIString(&ascii_obj);
                Py_DECREF(setter_name_obj);

                if (ascii)
                {
                    if (qstrlen(ascii) > 3 && ascii[0] == 's' &&
                            ascii[1] == 'e' && ascii[2] == 't' &&
                            ascii[3] == toupper(prop_name[0]) &&
                            qstrcmp(&ascii[4], &prop_name[1]) == 0)
                        prop_builder.setStdCppSet(true);
                }

                Py_DECREF(ascii_obj);
            }

            PyErr_Clear();
        }

        if (pp->pyqtprop_reset && PyCallable_Check(pp->pyqtprop_reset))
        {
            // Resetable.
            prop_builder.setResettable(true);
        }

        // Add the property flags.  Note that Qt4 always seems to have
        // ResolveEditable set but QMetaObjectBuilder doesn't provide an API
        // call to do it.
        prop_builder.setDesignable(pp->pyqtprop_flags & 0x00001000);
        prop_builder.setScriptable(pp->pyqtprop_flags & 0x00004000);
        prop_builder.setStored(pp->pyqtprop_flags & 0x00010000);
        prop_builder.setUser(pp->pyqtprop_flags & 0x00100000);
        prop_builder.setConstant(pp->pyqtprop_flags & 0x00000400);
        prop_builder.setFinal(pp->pyqtprop_flags & 0x00000800);

        prop_builder.setRevision(pp->pyqtprop_revision);

        // Save the property data for qt_metacall().  (We already have a
        // reference.)
        qo->pprops.append(pp);

        // We've finished with the property name.
        Py_DECREF(pprop.first);
    }

    // Initialise the rest of the meta-object.
    qo->mo = builder.toMetaObject();

    // Save the meta-object.
    pyqt_wt->metaobject = qo;

    return 0;
}
// Return the current Python context.  This should be called with the GIL.
int qpycore_current_context(const char **file, const char **function)
{
    static PyObject *currentframe = 0;
    static PyObject *getframeinfo = 0;
    static PyObject *saved_file = 0;
    static PyObject *saved_function = 0;

    PyObject *frame, *info, *file_obj, *linenr_obj, *function_obj;
    int linenr;

    frame = info = NULL;

    // Make sure we have what we need from the inspect module.
    if (!currentframe || !getframeinfo)
    {
        PyObject *inspect = PyImport_ImportModule("inspect");

        if (inspect)
        {
            if (!currentframe)
                currentframe = PyObject_GetAttrString(inspect, "currentframe");

            if (!getframeinfo)
                getframeinfo = PyObject_GetAttrString(inspect, "getframeinfo");

            Py_DECREF(inspect);
        }

        if (!currentframe || !getframeinfo)
            goto py_error;
    }

    // Get the current frame.
    if ((frame = PyObject_CallFunctionObjArgs(currentframe, NULL)) == NULL)
        goto py_error;

    // Get the frame details.
    if ((info = PyObject_CallFunctionObjArgs(getframeinfo, frame, NULL)) == NULL)
        goto py_error;;

    if ((file_obj = PyTuple_GetItem(info, 0)) == NULL)
        goto py_error;

    if ((linenr_obj = PyTuple_GetItem(info, 1)) == NULL)
        goto py_error;

    if ((function_obj = PyTuple_GetItem(info, 2)) == NULL)
        goto py_error;

    Py_XDECREF(saved_file);
#if PY_MAJOR_VERSION >= 3
    saved_file = PyUnicode_AsEncodedString(file_obj, "latin_1", "ignore");
#else
    saved_file = file_obj;
    Py_INCREF(saved_file);
#endif
    *file = SIPBytes_AS_STRING(saved_file);

    linenr = SIPLong_AsLong(linenr_obj);

    Py_XDECREF(saved_function);
#if PY_MAJOR_VERSION >= 3
    saved_function = PyUnicode_AsEncodedString(function_obj, "latin_1", "ignore");
#else
    saved_function = function_obj;
    Py_INCREF(saved_function);
#endif
    *function = SIPBytes_AS_STRING(saved_function);

    Py_DECREF(info);
    Py_DECREF(frame);

    return linenr;

py_error:
    Py_XDECREF(info);
    Py_XDECREF(frame);

    pyqt5_err_print();

    *file = *function = "";
    return 0;
}
Exemple #6
0
// Convert a Python object to a QVariant.
bool Chimera::fromPyObject(PyObject *py, QVariant *var, bool strict) const
{
    // Deal with the simple case of wrapping the Python object rather than
    // converting it.
    if (_type != sipType_QVariant && _metatype == PyQt_PyObject::metatype)
    {
        // If the type was specified by a Python type (as opposed to
        // 'PyQt_PyObject') then check the object is an instance of it.
        if (_py_type && !PyObject_IsInstance(py, _py_type))
            return false;

        *var = keep_as_pyobject(py);
        return true;
    }

    // Let any registered convertors have a go first.
    for (int i = 0; i < _registered_QVariant_convertors.count(); ++i)
    {
        bool ok;

        if (_registered_QVariant_convertors.at(i)(py, var, &ok))
            return ok;
    }

    int iserr = 0, value_class_state;
    void *ptr_class, *value_class = 0;

    // Temporary storage for different types.
    union {
        bool tmp_bool;
        int tmp_int;
        unsigned int tmp_unsigned_int;
        double tmp_double;
        void *tmp_void_ptr;
        long tmp_long;
        qlonglong tmp_qlonglong;
        short tmp_short;
        char tmp_char;
        unsigned long tmp_unsigned_long;
        qulonglong tmp_qulonglong;
        unsigned short tmp_unsigned_short;
        unsigned char tmp_unsigned_char;
        float tmp_float;
    } tmp_storage;

    void *variant_data = &tmp_storage;

    PyErr_Clear();

    QVariant variant;
    int metatype_used = _metatype;

    switch (_metatype)
    {
    case QMetaType::Bool:
        tmp_storage.tmp_bool = PyLong_AsLong(py);
        break;

    case QMetaType::Int:
        if (!_inexact || isEnum() || isFlag())
        {
            // It must fit into a C++ int.
#if PY_MAJOR_VERSION >= 3
            tmp_storage.tmp_int = PyLong_AsLong(py);
#else
            tmp_storage.tmp_int = PyInt_AsLong(py);
#endif
        }
        else
        {
            // Fit it into the smallest C++ type we can.

            qlonglong qll = PyLong_AsLongLong(py);

            if (PyErr_Occurred())
            {
                // Try again in case the value is unsigned and will fit with
                // the extra bit.

                PyErr_Clear();

                qulonglong qull = static_cast<qulonglong>(PyLong_AsUnsignedLongLong(py));

                if (PyErr_Occurred())
                {
                    // It won't fit into any C++ type so pass it as a Python
                    // object.

                    PyErr_Clear();

                    *var = keep_as_pyobject(py);
                    metatype_used = QMetaType::Void;
                }
                else
                {
                    tmp_storage.tmp_qulonglong = qull;
                    metatype_used = QMetaType::ULongLong;
                }
            }
            else if ((qlonglong)(int)qll == qll)
            {
                // It fits in a C++ int.
                tmp_storage.tmp_int = qll;
            }
            else if ((qulonglong)(unsigned int)qll == (qulonglong)qll)
            {
                // The extra bit is enough for it to fit.
                tmp_storage.tmp_unsigned_int = qll;
                metatype_used = QMetaType::UInt;
            }
            else
            {
                // This fits.
                tmp_storage.tmp_qlonglong = qll;
                metatype_used = QMetaType::LongLong;
            }
        }

        break;

    case QMetaType::UInt:
        tmp_storage.tmp_unsigned_int = sipLong_AsUnsignedLong(py);
        break;

    case QMetaType::Double:
        tmp_storage.tmp_double = PyFloat_AsDouble(py);
        break;

    case QMetaType::VoidStar:
        tmp_storage.tmp_void_ptr = sipConvertToVoidPtr(py);
        break;

    case QMetaType::Long:
        tmp_storage.tmp_long = PyLong_AsLong(py);
        break;

    case QMetaType::LongLong:
        tmp_storage.tmp_qlonglong = PyLong_AsLongLong(py);
        break;

    case QMetaType::Short:
        tmp_storage.tmp_short = PyLong_AsLong(py);
        break;

    case QMetaType::Char:
        if (SIPBytes_Check(py) && SIPBytes_GET_SIZE(py) == 1)
            tmp_storage.tmp_char = *SIPBytes_AS_STRING(py);
        else
            iserr = 1;
        break;

    case QMetaType::ULong:
        tmp_storage.tmp_unsigned_long = sipLong_AsUnsignedLong(py);
        break;

    case QMetaType::ULongLong:
        tmp_storage.tmp_qulonglong = static_cast<qulonglong>(PyLong_AsUnsignedLongLong(py));
        break;

    case QMetaType::UShort:
        tmp_storage.tmp_unsigned_short = sipLong_AsUnsignedLong(py);
        break;

    case QMetaType::UChar:
        if (SIPBytes_Check(py) && SIPBytes_GET_SIZE(py) == 1)
            tmp_storage.tmp_unsigned_char = *SIPBytes_AS_STRING(py);
        else
            iserr = 1;
        break;

    case QMetaType::Float:
        tmp_storage.tmp_float = PyFloat_AsDouble(py);
        break;

    case QMetaType::QObjectStar:
        tmp_storage.tmp_void_ptr = sipForceConvertToType(py, sipType_QObject,
                0, SIP_NO_CONVERTORS, 0, &iserr);
        break;

    case QMetaType::QWidgetStar:
        if (sipType_QWidget)
        {
            tmp_storage.tmp_void_ptr = sipForceConvertToType(py,
                    sipType_QWidget, 0, SIP_NO_CONVERTORS, 0, &iserr);
        }
        else
        {
            iserr = 1;
        }

        break;

    case QMetaType::QVariantList:
        {
            QVariantList ql;

            if (to_QVariantList(py, ql))
            {
                *var = QVariant(ql);
                metatype_used = QMetaType::Void;
            }
            else
            {
                iserr = 1;
            }

            break;
        }

    case QMetaType::QVariantMap:
        {
            QVariantMap qm;

            if (to_QVariantMap(py, qm))
            {
                *var = QVariant(qm);
                metatype_used = QMetaType::Void;
            }
            else if (strict)
            {
                iserr = 1;
            }
            else
            {
                // Assume the failure is because the key was the wrong type.
                PyErr_Clear();

                *var = keep_as_pyobject(py);
                metatype_used = QMetaType::Void;
            }

            break;
        }

#if QT_VERSION >= 0x040500
    case QMetaType::QVariantHash:
        {
            QVariantHash qh;

            if (to_QVariantHash(py, qh))
            {
                *var = QVariant(qh);
                metatype_used = QMetaType::Void;
            }
            else
            {
                iserr = 1;
            }

            break;
        }
#endif

    case -1:
        metatype_used = QMetaType::VoidStar;

        if (SIPBytes_Check(py))
            tmp_storage.tmp_void_ptr = SIPBytes_AS_STRING(py);
        else if (py == Py_None)
            tmp_storage.tmp_void_ptr = 0;
        else
            iserr = 1;

        break;

    default:
        if (_type)
        {
            if (_name.endsWith('*'))
            {
                ptr_class = sipForceConvertToType(py, _type, 0,
                        SIP_NO_CONVERTORS, 0, &iserr);

                variant_data = &ptr_class;
            }
            else
            {
                value_class = sipForceConvertToType(py, _type, 0,
                    SIP_NOT_NONE, &value_class_state, &iserr);

                variant_data = value_class;
            }
        }
        else
        {
            // This is a class we don't recognise.
            iserr = 1;
        }
    }

    if (iserr || PyErr_Occurred())
    {
        PyErr_Format(PyExc_TypeError,
                "unable to convert a Python '%s' object to a C++ '%s' instance",
                Py_TYPE(py)->tp_name, _name.constData());

        iserr = 1;
    }
    else if (_type == sipType_QVariant)
    {
        *var = QVariant(*reinterpret_cast<QVariant *>(variant_data));
    }
    else if (metatype_used != QMetaType::Void)
    {
        *var = QVariant(metatype_used, variant_data);
    }

    // Release any temporary value-class instance now that QVariant will have
    // made a copy.
    if (value_class)
        sipReleaseType(value_class, _type, value_class_state);

    return (iserr == 0);
}
Exemple #7
0
// Convert a Python object to C++ at a given address.  This has a lot in common
// with the method that converts to a QVariant.  However, unlike that method,
// we have no control over the size of the destination storage and so must
// convert exactly as requested.
bool Chimera::fromPyObject(PyObject *py, void *cpp) const
{
    // Let any registered convertors have a go first.
    for (int i = 0; i < _registered_QVariant_data_convertors.count(); ++i)
    {
        bool ok;

        if (_registered_QVariant_data_convertors.at(i)(py, cpp, _metatype, &ok))
            return ok;
    }

    int iserr = 0;

    PyErr_Clear();

    switch (_metatype)
    {
    case QMetaType::Bool:
        *reinterpret_cast<bool *>(cpp) = PyLong_AsLong(py);
        break;

    case QMetaType::Int:
        // Truncate it if necessary to fit into a C++ int.  This will
        // automatically handle enums and flag types as Python knows how to
        // convert them to ints.
#if PY_MAJOR_VERSION >= 3
        *reinterpret_cast<int *>(cpp) = PyLong_AsLong(py);
#else
        *reinterpret_cast<int *>(cpp) = PyInt_AsLong(py);
#endif
        break;

    case QMetaType::UInt:
        *reinterpret_cast<unsigned int *>(cpp) = sipLong_AsUnsignedLong(py);
        break;

    case QMetaType::Double:
        *reinterpret_cast<double *>(cpp) = PyFloat_AsDouble(py);
        break;

    case QMetaType::VoidStar:
        *reinterpret_cast<void **>(cpp) = sipConvertToVoidPtr(py);
        break;

    case QMetaType::Long:
        *reinterpret_cast<long *>(cpp) = PyLong_AsLong(py);
        break;

    case QMetaType::LongLong:
        *reinterpret_cast<qlonglong *>(cpp) = PyLong_AsLongLong(py);
        break;

    case QMetaType::Short:
        *reinterpret_cast<short *>(cpp) = PyLong_AsLong(py);
        break;

    case QMetaType::Char:
        if (SIPBytes_Check(py) && SIPBytes_GET_SIZE(py) == 1)
            *reinterpret_cast<char *>(cpp) = *SIPBytes_AS_STRING(py);
        else
            iserr = 1;
        break;

    case QMetaType::ULong:
        *reinterpret_cast<unsigned long *>(cpp) = sipLong_AsUnsignedLong(py);
        break;

    case QMetaType::ULongLong:
        *reinterpret_cast<qulonglong *>(cpp) = static_cast<qulonglong>(PyLong_AsUnsignedLongLong(py));
        break;

    case QMetaType::UShort:
        *reinterpret_cast<unsigned short *>(cpp) = sipLong_AsUnsignedLong(py);
        break;

    case QMetaType::UChar:
        if (SIPBytes_Check(py) && SIPBytes_GET_SIZE(py) == 1)
            *reinterpret_cast<unsigned char *>(cpp) = *SIPBytes_AS_STRING(py);
        else
            iserr = 1;
        break;

    case QMetaType::Float:
        *reinterpret_cast<float *>(cpp) = PyFloat_AsDouble(py);
        break;

    case QMetaType::QObjectStar:
        *reinterpret_cast<void **>(cpp) = sipForceConvertToType(py,
                sipType_QObject, 0, SIP_NO_CONVERTORS, 0, &iserr);
        break;

    case QMetaType::QWidgetStar:
        if (sipType_QWidget)
        {
            *reinterpret_cast<void **>(cpp) = sipForceConvertToType(py,
                    sipType_QWidget, 0, SIP_NO_CONVERTORS, 0, &iserr);
        }
        else
        {
            iserr = 1;
        }

        break;

    case QMetaType::QVariantList:
        {
            QVariantList ql;

            if (to_QVariantList(py, ql))
                *reinterpret_cast<QVariantList *>(cpp) = ql;
            else
                iserr = 1;

            break;
        }

    case QMetaType::QVariantMap:
        {
            QVariantMap qm;

            if (to_QVariantMap(py, qm))
                *reinterpret_cast<QVariantMap *>(cpp) = qm;
            else
                iserr = 1;

            break;
        }

#if QT_VERSION >= 0x040500
    case QMetaType::QVariantHash:
        {
            QVariantHash qh;

            if (to_QVariantHash(py, qh))
                *reinterpret_cast<QVariantHash *>(cpp) = qh;
            else
                iserr = 1;

            break;
        }
#endif

    case -1:
        {
            char **ptr = reinterpret_cast<char **>(cpp);

            if (SIPBytes_Check(py))
                *ptr = SIPBytes_AS_STRING(py);
            else if (py == Py_None)
                *ptr = 0;
            else
                iserr = 1;

            break;
        }

    default:
        if (_type)
        {
            if (_name.endsWith('*'))
            {
                // This must be a pointer-type.

                *reinterpret_cast<void **>(cpp) = sipForceConvertToType(py,
                        _type, 0, SIP_NO_CONVERTORS, 0, &iserr);
            }
            else
            {
                // This must be a value-type.

                sipAssignFunc assign = get_assign_helper();

                if (assign)
                {
                    int state;
                    void *value_class;

                    value_class = sipForceConvertToType(py, _type, 0,
                            SIP_NOT_NONE, &state, &iserr);

                    if (!iserr)
                        assign(cpp, 0, value_class);

                    sipReleaseType(value_class, _type, state);
                }
                else
                {
                    iserr = 1;
                }
            }
        }
        else
        {
            iserr = 1;
        }
    }

    if (iserr || PyErr_Occurred())
    {
        PyErr_Format(PyExc_TypeError,
                "unable to convert a Python '%s' object to a C++ '%s' instance",
                Py_TYPE(py)->tp_name, _name.constData());

        return false;
    }

    return true;
}