// 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; }
static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) { // Set classname builder.setClassName(ignoreEnd->className()); // Clone Q_CLASSINFO for (int ii = mo->classInfoOffset(); ii < mo->classInfoCount(); ++ii) { QMetaClassInfo info = mo->classInfo(ii); int otherIndex = ignoreEnd->indexOfClassInfo(info.name()); if (otherIndex >= ignoreStart->classInfoOffset() + ignoreStart->classInfoCount()) { // Skip } else { builder.addClassInfo(info.name(), info.value()); } } // Clone Q_PROPERTY for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { QMetaProperty property = mo->property(ii); int otherIndex = ignoreEnd->indexOfProperty(property.name()); if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void")); // Skip } else { builder.addProperty(property); } } // Clone Q_METHODS for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) { QMetaMethod method = mo->method(ii); // More complex - need to search name QByteArray name = method.name(); bool found = false; for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); ++ii) { QMetaMethod other = ignoreEnd->method(ii); found = name == other.name(); } QMetaMethodBuilder m = builder.addMethod(method); if (found) // SKIP m.setAccess(QMetaMethod::Private); } // Clone Q_ENUMS for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) { QMetaEnum enumerator = mo->enumerator(ii); int otherIndex = ignoreEnd->indexOfEnumerator(enumerator.name()); if (otherIndex >= ignoreStart->enumeratorOffset() + ignoreStart->enumeratorCount()) { // Skip } else { builder.addEnumerator(enumerator); } } }