diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index 773dcea..dde23da 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -431,6 +431,30 @@ PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtCla return result; } +PyObject* PythonQtPrivate::createNewPythonQtEnumWrapper(const char* enumName, PyObject* parentObject) { + PyObject* result; + + PyObject* className = PyString_FromString(enumName); + + PyObject* baseClasses = PyTuple_New(1); + PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PyInt_Type); + + PyObject* module = PyObject_GetAttrString(parentObject, "__module__"); + PyObject* typeDict = PyDict_New(); + PyDict_SetItemString(typeDict, "__module__", module); + + PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict); + + // create the new int derived type object by calling the core type + result = PyObject_Call((PyObject *)&PyType_Type, args, NULL); + + Py_DECREF(baseClasses); + Py_DECREF(typeDict); + Py_DECREF(args); + Py_DECREF(className); + + return result; +} PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj) { diff --git a/src/PythonQt.h b/src/PythonQt.h index 649a362..5413578 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -446,6 +446,9 @@ public: //! helper method that creates a PythonQtClassWrapper object PythonQtClassWrapper* createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package = NULL); + //! helper that creates a new int derived class that represents the enum of the given name + PyObject* createNewPythonQtEnumWrapper(const char* enumName, PyObject* parentObject); + //! helper method that creates a PythonQtInstanceWrapper object and registers it in the object map PythonQtInstanceWrapper* createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr = NULL); diff --git a/src/PythonQtClassInfo.cpp b/src/PythonQtClassInfo.cpp index cd7abcb..1f4ab0e 100644 --- a/src/PythonQtClassInfo.cpp +++ b/src/PythonQtClassInfo.cpp @@ -43,6 +43,8 @@ #include "PythonQtMethodInfo.h" #include "PythonQt.h" #include +#include +#include QHash PythonQtMethodInfo::_parameterTypeDict; @@ -56,6 +58,7 @@ PythonQtClassInfo::PythonQtClassInfo() { _shellSetInstanceWrapperCB = NULL; _metaTypeId = -1; _isQObject = false; + _enumsCreated = false; } PythonQtClassInfo::~PythonQtClassInfo() @@ -250,13 +253,23 @@ bool PythonQtClassInfo::lookForEnumAndCache(const QMetaObject* meta, const char* QMetaEnum e = meta->enumerator(i); for (int j=0; j < e.keyCount(); j++) { if (qstrcmp(e.key(j), memberName)==0) { - PythonQtMemberInfo newInfo(e.value(j)); - _cachedMembers.insert(memberName, newInfo); + PyObject* enumType = findEnumWrapper(e.name()); + if (enumType) { + PyObject* args = Py_BuildValue("(i)", e.value(j)); + PyObject* enumValue = PyObject_Call(enumType, args, NULL); + Py_DECREF(args); + PythonQtObjectPtr enumValuePtr; + enumValuePtr.setNewRef(enumValue); + PythonQtMemberInfo newInfo(enumValuePtr); + _cachedMembers.insert(memberName, newInfo); #ifdef PYTHONQT_DEBUG - std::cout << "caching enum " << memberName << " on " << meta->className() << std::endl; + std::cout << "caching enum " << memberName << " on " << meta->className() << std::endl; #endif - found = true; - break; + found = true; + break; + } else { + std::cout << "enum " << e.name() << " not found on " << className() << std::endl; + } } } } @@ -295,6 +308,15 @@ PythonQtMemberInfo PythonQtClassInfo::member(const char* memberName) } } if (!found) { + PyObject* p = findEnumWrapper(memberName); + if (p) { + info._type = PythonQtMemberInfo::EnumWrapper; + info._enumWrapper = p; + _cachedMembers.insert(memberName, info); + found = true; + } + } + if (!found) { // we store a NotFound member, so that we get a quick result for non existing members (e.g. operator_equal lookup) info._type = PythonQtMemberInfo::NotFound; _cachedMembers.insert(memberName, info); @@ -470,6 +492,7 @@ QStringList PythonQtClassInfo::memberList(bool metaOnly) foreach(const QMetaObject* meta, enumMetaObjects) { for (int i = 0; ienumeratorCount(); i++) { QMetaEnum e = meta->enumerator(i); + l << e.name(); for (int j=0; j < e.keyCount(); j++) { l << QString(e.key(j)); } @@ -661,6 +684,9 @@ QObject* PythonQtClassInfo::decorator() PythonQt::priv()->addDecorators(_decoratorProvider, PythonQtPrivate::ConstructorDecorator | PythonQtPrivate::DestructorDecorator); } } + if (!_enumsCreated) { + createEnumWrappers(); + } return _decoratorProvider; } @@ -768,3 +794,43 @@ bool PythonQtClassInfo::hasEnum(const QByteArray& name) return found; } +void PythonQtClassInfo::createEnumWrappers(const QMetaObject* meta) +{ + for (int i = meta->enumeratorOffset();ienumeratorCount();i++) { + QMetaEnum e = meta->enumerator(i); + PythonQtObjectPtr p; + p.setNewRef(PythonQt::priv()->createNewPythonQtEnumWrapper(e.name(), _pythonQtClassWrapper)); + _enumWrappers.append(p); + } +} + +void PythonQtClassInfo::createEnumWrappers() +{ + if (!_enumsCreated) { + _enumsCreated = true; + if (_meta) { + createEnumWrappers(_meta); + } + if (decorator()) { + createEnumWrappers(decorator()->metaObject()); + } + foreach(const ParentClassInfo& info, _parentClasses) { + info._parent->createEnumWrappers(); + } + } +} + +PyObject* PythonQtClassInfo::findEnumWrapper(const char* name) { + foreach(const PythonQtObjectPtr& p, _enumWrappers) { + const char* className = ((PyTypeObject*)p.object())->tp_name; + if (qstrcmp(className, name)==0) { + return p.object(); + } + } + foreach(const ParentClassInfo& info, _parentClasses) { + PyObject* p = info._parent->findEnumWrapper(name); + if (p) return p; + } + return NULL; +} + diff --git a/src/PythonQtClassInfo.h b/src/PythonQtClassInfo.h index 30838fa..d4bd73d 100644 --- a/src/PythonQtClassInfo.h +++ b/src/PythonQtClassInfo.h @@ -44,35 +44,38 @@ class PythonQtSlotInfo; struct PythonQtMemberInfo { enum Type { - Invalid, Slot, EnumValue, Property, NotFound + Invalid, Slot, EnumValue, EnumWrapper, Property, NotFound }; - PythonQtMemberInfo():_type(Invalid),_slot(NULL),_enumValue(0) { } + PythonQtMemberInfo():_type(Invalid),_slot(NULL),_enumWrapper(NULL),_enumValue(0) { } PythonQtMemberInfo(PythonQtSlotInfo* info) { _type = Slot; _slot = info; - _enumValue = 0; + _enumValue = NULL; } - PythonQtMemberInfo(unsigned int enumValue) { + PythonQtMemberInfo(const PythonQtObjectPtr& enumValue) { _type = EnumValue; _slot = NULL; _enumValue = enumValue; + _enumWrapper = NULL; } PythonQtMemberInfo(const QMetaProperty& prop) { _type = Property; _slot = NULL; - _enumValue = 0; + _enumValue = NULL; _property = prop; + _enumWrapper = NULL; } Type _type; // TODO: this could be a union... PythonQtSlotInfo* _slot; - unsigned int _enumValue; + PyObject* _enumWrapper; + PythonQtObjectPtr _enumValue; QMetaProperty _property; }; @@ -194,7 +197,11 @@ public: //! returns if the localScope has an enum of that type name or if the enum contains a :: scope, if that class contails the enum static bool hasEnum(const QByteArray& name, PythonQtClassInfo* localScope); -private: +private: + void createEnumWrappers(); + void createEnumWrappers(const QMetaObject* meta); + PyObject* findEnumWrapper(const char* name); + //! checks if the enum is part of this class (without any leading scope!) bool hasEnum(const QByteArray& name); @@ -223,6 +230,8 @@ private: PythonQtSlotInfo* _destructor; QList _decoratorSlots; + QList _enumWrappers; + const QMetaObject* _meta; QByteArray _wrappedClassName; @@ -240,6 +249,7 @@ private: int _metaTypeId; bool _isQObject; + bool _enumsCreated; }; diff --git a/src/PythonQtClassWrapper.cpp b/src/PythonQtClassWrapper.cpp index 9df4763..976744f 100644 --- a/src/PythonQtClassWrapper.cpp +++ b/src/PythonQtClassWrapper.cpp @@ -183,9 +183,14 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj, PyObject *name) if (wrapper->classInfo()) { PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName); if (member._type == PythonQtMemberInfo::EnumValue) { - return PyInt_FromLong(member._enumValue); - } else - if (member._type == PythonQtMemberInfo::Slot) { + PyObject* enumValue = member._enumValue; + Py_INCREF(enumValue); + return enumValue; + } else if (member._type == PythonQtMemberInfo::EnumWrapper) { + PyObject* enumWrapper = member._enumWrapper; + Py_INCREF(enumWrapper); + return enumWrapper; + } else if (member._type == PythonQtMemberInfo::Slot) { // we return all slots, even the instance slots, since they are callable as unbound slots with self argument return PythonQtSlotFunction_New(member._slot, obj, NULL); } diff --git a/src/PythonQtConversion.cpp b/src/PythonQtConversion.cpp index a958710..a29c488 100644 --- a/src/PythonQtConversion.cpp +++ b/src/PythonQtConversion.cpp @@ -611,7 +611,10 @@ int PythonQtConv::PyObjGetInt(PyObject* val, bool strict, bool &ok) { if (val->ob_type == &PyInt_Type) { d = PyInt_AS_LONG(val); } else if (!strict) { - if (val->ob_type == &PyFloat_Type) { + if (PyObject_TypeCheck(val, &PyInt_Type)) { + // support for derived int classes, e.g. for our enums + d = PyInt_AS_LONG(val); + } else if (val->ob_type == &PyFloat_Type) { d = floor(PyFloat_AS_DOUBLE(val)); } else if (val->ob_type == &PyLong_Type) { // handle error on overflow! @@ -632,7 +635,7 @@ int PythonQtConv::PyObjGetInt(PyObject* val, bool strict, bool &ok) { qint64 PythonQtConv::PyObjGetLongLong(PyObject* val, bool strict, bool &ok) { qint64 d = 0; ok = true; - if (val->ob_type == &PyInt_Type) { + if (PyObject_TypeCheck(val, &PyInt_Type)) { d = PyInt_AS_LONG(val); } else if (val->ob_type == &PyLong_Type) { d = PyLong_AsLongLong(val); @@ -655,7 +658,7 @@ qint64 PythonQtConv::PyObjGetLongLong(PyObject* val, bool strict, bool &ok) { quint64 PythonQtConv::PyObjGetULongLong(PyObject* val, bool strict, bool &ok) { quint64 d = 0; ok = true; - if (val->ob_type == &PyInt_Type) { + if (PyObject_TypeCheck(val, &PyInt_Type)) { d = PyInt_AS_LONG(val); } else if (val->ob_type == &PyLong_Type) { d = PyLong_AsLongLong(val); @@ -681,7 +684,7 @@ double PythonQtConv::PyObjGetDouble(PyObject* val, bool strict, bool &ok) { if (val->ob_type == &PyFloat_Type) { d = PyFloat_AS_DOUBLE(val); } else if (!strict) { - if (val->ob_type == &PyInt_Type) { + if (PyObject_TypeCheck(val, &PyInt_Type)) { d = PyInt_AS_LONG(val); } else if (val->ob_type == &PyLong_Type) { d = PyLong_AsLong(val); @@ -707,7 +710,7 @@ QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type) // no special type requested if (val->ob_type==&PyString_Type || val->ob_type==&PyUnicode_Type) { type = QVariant::String; - } else if (val->ob_type==&PyInt_Type) { + } else if (PyObject_TypeCheck(val, &PyInt_Type)) { type = QVariant::Int; } else if (val->ob_type==&PyLong_Type) { type = QVariant::LongLong; diff --git a/src/PythonQtInstanceWrapper.cpp b/src/PythonQtInstanceWrapper.cpp index a2747ea..9c6b729 100644 --- a/src/PythonQtInstanceWrapper.cpp +++ b/src/PythonQtInstanceWrapper.cpp @@ -277,7 +277,14 @@ static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name) return PythonQtSlotFunction_New(member._slot, obj, NULL); break; case PythonQtMemberInfo::EnumValue: - return PyInt_FromLong(member._enumValue); + PyObject* enumValue = member._enumValue; + Py_INCREF(enumValue); + return enumValue; + break; + case PythonQtMemberInfo::EnumWrapper: + PyObject* enumWrapper = member._enumWrapper; + Py_INCREF(enumWrapper); + return enumWrapper; break; default: // is an invalid type, go on @@ -353,6 +360,8 @@ static int PythonQtInstanceWrapper_setattro(PyObject *obj,PyObject *name,PyObjec error = QString("Slot '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object"; } else if (member._type == PythonQtMemberInfo::EnumValue) { error = QString("EnumValue '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object"; + } else if (member._type == PythonQtMemberInfo::EnumWrapper) { + error = QString("Enum '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object"; } else if (member._type == PythonQtMemberInfo::NotFound) { // if we are a derived python class, we allow setting attributes. // if we are a direct CPP wrapper, we do NOT allow it, since