From 44e5ff2700cb8c7d2b245ec330a8f05f523bbf22 2009-04-14 14:52:30 From: florianlink Date: 2009-04-14 14:52:30 Subject: [PATCH] - wrapped instances are now wrapped by class specific subtypes to facilitate future deriving from python - object creation has changed by using the python type system (calling the type object with PyObject_Call) git-svn-id: svn://svn.code.sf.net/p/pythonqt/code/trunk@54 ea8d5007-eb21-0410-b261-ccb3ea6e24a9 --- diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index 92061ca..cfa7b85 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -170,17 +170,20 @@ PythonQt::PythonQt(int flags) } Py_INCREF(&PythonQtSlotFunction_Type); - // add our own python object types for qt objects - if (PyType_Ready(&PythonQtInstanceWrapper_Type) < 0) { - std::cerr << "could not initialize PythonQtInstanceWrapper_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; - } - Py_INCREF(&PythonQtInstanceWrapper_Type); - - // add our own python object types for qt objects + // according to Python docs, set the type late here, since it can not safely be stored in the struct when declaring it + PythonQtClassWrapper_Type.tp_base = &PyType_Type; + // add our own python object types for classes if (PyType_Ready(&PythonQtClassWrapper_Type) < 0) { std::cerr << "could not initialize PythonQtClassWrapper_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; } Py_INCREF(&PythonQtClassWrapper_Type); + + // add our own python object types for CPP instances + if (PyType_Ready(&PythonQtInstanceWrapper_Type) < 0) { + PythonQt::handleError(); + std::cerr << "could not initialize PythonQtInstanceWrapper_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; + } + Py_INCREF(&PythonQtInstanceWrapper_Type); // add our own python object types for redirection of stdout if (PyType_Ready(&PythonQtStdOutRedirectType) < 0) { @@ -274,16 +277,8 @@ void PythonQtPrivate::registerClass(const QMetaObject* metaobject, const char* p while (m) { PythonQtClassInfo* info = _knownQtClasses.value(m->className()); if (!info) { - info = new PythonQtClassInfo(m); + info = createPythonQtClassInfo(m, NULL, package); _knownQtClasses.insert(m->className(), info); - PythonQtObjectPtr pack = packageByName(package); - PyObject* pyobj = (PyObject*)createNewPythonQtClassWrapper(info); - PyModule_AddObject(pack, m->className(), pyobj); - if (package && strncmp(package,"Qt",2)==0) { - // put all qt objects into Qt as well - PythonQtObjectPtr pack = packageByName("Qt"); - PyModule_AddObject(pack, m->className(), pyobj); - } } if (first) { first = false; @@ -295,6 +290,21 @@ void PythonQtPrivate::registerClass(const QMetaObject* metaobject, const char* p } } +PythonQtClassInfo* PythonQtPrivate::createPythonQtClassInfo(const QMetaObject* meta, const char* cppClassName, const char* package) +{ + PythonQtClassInfo* info = new PythonQtClassInfo(meta, cppClassName); + PythonQtObjectPtr pack = packageByName(package); + PyObject* pyobj = (PyObject*)createNewPythonQtClassWrapper(info, package); + PyModule_AddObject(pack, meta?meta->className():cppClassName, pyobj); + if (package && strncmp(package,"Qt",2)==0) { + // put all qt objects into Qt as well + PythonQtObjectPtr pack = packageByName("Qt"); + PyModule_AddObject(pack, meta?meta->className():cppClassName, pyobj); + } + info->setPythonQtClassWrapper(pyobj); + return info; +} + bool PythonQtPrivate::isEnumType(const QMetaObject* meta, const QByteArray& name) { int i = meta?meta->indexOfEnumerator(name.constData()):-1; if (i!=-1) { @@ -337,10 +347,10 @@ PyObject* PythonQtPrivate::wrapQObject(QObject* obj) classInfo = _knownQtClasses.value(obj->metaObject()->className()); } wrap = createNewPythonQtInstanceWrapper(obj, classInfo); - // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1()); + // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); } else { Py_INCREF(wrap); - // mlabDebugConst("MLABPython","qobject wrapper reused " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1()); + // mlabDebugConst("MLABPython","qobject wrapper reused " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); } return (PyObject*)wrap; } @@ -371,7 +381,7 @@ PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name) info = _knownQtClasses.value(qptr->metaObject()->className()); } wrap = createNewPythonQtInstanceWrapper(qptr, info); - // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1()); + // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); } else { // maybe it is a PyObject, which we can return directly if (name == "PyObject") { @@ -389,32 +399,37 @@ PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name) } PythonQtClassInfo* info = _knownQtWrapperClasses.value(name); if (!info) { - info = new PythonQtClassInfo(wrapper?wrapper->metaObject():NULL, name); - _knownQtWrapperClasses.insert(name, info); - PyModule_AddObject(_pythonQtModule, name, (PyObject*)createNewPythonQtClassWrapper(info)); - } else { - if (wrapper && (info->metaObject() != wrapper->metaObject())) { - info->setMetaObject(wrapper->metaObject()); - } + registerCPPClass(name.constData()); + info = _knownQtWrapperClasses.value(name); + } + if (wrapper && (info->metaObject() != wrapper->metaObject())) { + info->setMetaObject(wrapper->metaObject()); } wrap = createNewPythonQtInstanceWrapper(wrapper, info, ptr); - // mlabDebugConst("MLABPython","new c++ wrapper added " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1()); + // mlabDebugConst("MLABPython","new c++ wrapper added " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); } } else { Py_INCREF(wrap); - //mlabDebugConst("MLABPython","c++ wrapper reused " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1()); + //mlabDebugConst("MLABPython","c++ wrapper reused " << wrap->_wrappedPtr << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); } return (PyObject*)wrap; } +PyObject* PythonQtPrivate::dummyTuple() { + static PyObject* dummyTuple = NULL; + if (dummyTuple==NULL) { + dummyTuple = PyTuple_New(1); + PyTuple_SET_ITEM(dummyTuple, 0 , PyString_FromString("dummy")); + } + return dummyTuple; +} + PythonQtInstanceWrapper* PythonQtPrivate::createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr) { - PythonQtInstanceWrapper* result; - result = (PythonQtInstanceWrapper *)PythonQtInstanceWrapper_Type.tp_new(&PythonQtInstanceWrapper_Type, - NULL, NULL); + // call the associated class type to create a new instance... + PythonQtInstanceWrapper* result = (PythonQtInstanceWrapper*)PyObject_Call(info->pythonQtClassWrapper(), dummyTuple(), NULL); result->setQObject(obj); - result->_info = info; result->_wrappedPtr = wrappedPtr; result->_ownedByPythonQt = false; result->_useQMetaTypeDestroy = false; @@ -431,11 +446,36 @@ PythonQtInstanceWrapper* PythonQtPrivate::createNewPythonQtInstanceWrapper(QObje return result; } -PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtClassInfo* info) { +PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package) { PythonQtClassWrapper* result; - result = (PythonQtClassWrapper *)PythonQtClassWrapper_Type.tp_new(&PythonQtClassWrapper_Type, - NULL, NULL); - result->_info = info; + + PyObject* className = PyString_FromString(info->className()); + + PyObject* baseClasses = PyTuple_New(1); + PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PythonQtInstanceWrapper_Type); + + PyObject* typeDict = PyDict_New(); + QByteArray moduleName("PythonQt"); + if (package && strcmp(package, "")!=0) { + moduleName += "."; + moduleName += package; + } + PyDict_SetItemString(typeDict, "__module__", PyString_FromString(moduleName.constData())); + + PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict); + + // set the class info so that PythonQtClassWrapper_new can read it + _currentClassInfoForClassWrapperCreation = info; + // create the new type object by calling the type + result = (PythonQtClassWrapper *)PyObject_Call((PyObject *)&PythonQtClassWrapper_Type, args, NULL); + + Py_DECREF(baseClasses); + Py_DECREF(typeDict); + Py_DECREF(args); + Py_DECREF(className); + + //TODO XXX why is this incref needed? It looks like the types get garbage collected somehow?! + Py_INCREF((PyObject*)result); return result; } @@ -666,7 +706,7 @@ QStringList PythonQt::introspection(PyObject* module, const QString& objectname, } } else if (object->ob_type == &PythonQtClassWrapper_Type) { PythonQtClassWrapper* o = (PythonQtClassWrapper*)object.object(); - PythonQtSlotInfo* info = o->_info->constructors(); + PythonQtSlotInfo* info = o->classInfo()->constructors(); while (info) { results << info->fullSignature(false); @@ -817,16 +857,6 @@ void PythonQt::addWrapperFactory(PythonQtCppWrapperFactory* factory) _p->_cppWrapperFactories.append(factory); } -void PythonQt::addConstructorHandler(PythonQtConstructorHandler* factory) -{ - _p->_constructorHandlers.append(factory); -} - -const QList& PythonQt::constructorHandlers() -{ - return _p->_constructorHandlers; -}; - //--------------------------------------------------------------------------------------------------- PythonQtPrivate::PythonQtPrivate() { @@ -834,6 +864,14 @@ PythonQtPrivate::PythonQtPrivate() _defaultImporter = new PythonQtQFileImporter; _noLongerWrappedCB = NULL; _wrappedCB = NULL; + _currentClassInfoForClassWrapperCreation = NULL; +} + +PythonQtClassInfo* PythonQtPrivate::currentClassInfoForClassWrapperCreation() +{ + PythonQtClassInfo* info = _currentClassInfoForClassWrapperCreation; + _currentClassInfoForClassWrapperCreation = NULL; + return info; } void PythonQtPrivate::addDecorators(QObject* o, int decoTypes) @@ -1006,16 +1044,8 @@ void PythonQtPrivate::registerCPPClass(const char* typeName, const char* parentT { PythonQtClassInfo* info = _knownQtWrapperClasses.value(typeName); if (!info) { - info = new PythonQtClassInfo(NULL, typeName); + info = createPythonQtClassInfo(NULL, typeName, package); _knownQtWrapperClasses.insert(typeName, info); - PythonQtObjectPtr pack = packageByName(package); - PyObject* pyobj = (PyObject*)createNewPythonQtClassWrapper(info); - PyModule_AddObject(pack, typeName, pyobj); - if (package && strncmp(package,"Qt",2)==0) { - // put all qt objects into Qt as well - PythonQtObjectPtr pack = packageByName("Qt"); - PyModule_AddObject(pack, typeName, pyobj); - } } if (parentTypeName) { info->setWrappedParentClassName(parentTypeName); @@ -1055,6 +1085,11 @@ void PythonQtPrivate::removeWrapperPointer(void* obj) _wrappedObjects.remove(obj); } +void PythonQtPrivate::addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper) +{ + _wrappedObjects.insert(obj, wrapper); +} + PythonQtInstanceWrapper* PythonQtPrivate::findWrapperAndRemoveUnused(void* obj) { PythonQtInstanceWrapper* wrap = _wrappedObjects.value(obj); diff --git a/src/PythonQt.h b/src/PythonQt.h index 04e3a8b..1607add 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -63,7 +63,6 @@ class PythonQtMethodInfo; class PythonQtSignalReceiver; class PythonQtImportFileInterface; class PythonQtCppWrapperFactory; -class PythonQtConstructorHandler; class PythonQtQFileImporter; typedef void PythonQtQObjectWrappedCB(QObject* object); @@ -269,12 +268,6 @@ public: //! add the given factory to PythonQt (ownership stays with caller) void addWrapperFactory(PythonQtCppWrapperFactory* factory); - //! add the given constructor handler to PythonQt (ownership stays with caller) - void addConstructorHandler(PythonQtConstructorHandler* handler); - - //! get list of constructor handlers - const QList& constructorHandlers(); - //@} //@{ Custom importer (to replace internal import implementation of python) @@ -383,6 +376,8 @@ public: //! returns if the id is the id for PythonQtObjectPtr bool isPythonQtObjectPtrMetaId(int id) { return _PythonQtObjectPtr_metaId == id; } + //! add the wrapper pointer (for reuse if the same obj appears while wrapper still exists) + void addWrapperPointer(void* obj, PythonQtInstanceWrapper* wrapper); //! remove the wrapper ptr again void removeWrapperPointer(void* obj); @@ -425,7 +420,7 @@ public: bool isEnumType(const QMetaObject* meta, const QByteArray& name); //! helper method that creates a PythonQtClassWrapper object - PythonQtClassWrapper* createNewPythonQtClassWrapper(PythonQtClassInfo* info); + PythonQtClassWrapper* createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package = NULL); //! helper method that creates a PythonQtInstanceWrapper object and registers it in the object map PythonQtInstanceWrapper* createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr = NULL); @@ -445,7 +440,17 @@ public: //! creates the new module from the given pycode PythonQtObjectPtr createModule(const QString& name, PyObject* pycode); + //! get the current class info (for the next PythonQtClassWrapper that is created) and reset it to NULL again + PythonQtClassInfo* currentClassInfoForClassWrapperCreation(); + + //! the dummy tuple (which is empty and may be used to detected that a wrapper is called from internal wrapper creation + static PyObject* dummyTuple(); + private: + + //! create a new class info and python wrapper type + PythonQtClassInfo* createPythonQtClassInfo(const QMetaObject* meta, const char* cppClassName, const char* package); + //! get/create new package module PythonQtObjectPtr packageByName(const char* name); @@ -487,14 +492,13 @@ private: //! the cpp object wrapper factories QList _cppWrapperFactories; - //! the cpp object wrapper factories - QList _constructorHandlers; - QHash _constructorSlots; QHash _destructorSlots; QHash _packages; + PythonQtClassInfo* _currentClassInfoForClassWrapperCreation; + int _initFlags; int _PythonQtObjectPtr_metaId; diff --git a/src/PythonQtClassInfo.cpp b/src/PythonQtClassInfo.cpp index 7e4eb0f..89c697a 100644 --- a/src/PythonQtClassInfo.cpp +++ b/src/PythonQtClassInfo.cpp @@ -54,6 +54,7 @@ PythonQtClassInfo::PythonQtClassInfo(const QMetaObject* meta, const QByteArray& _parentClassInfoResolved = false; _decoratorProvider = NULL; _decoratorProviderCB = NULL; + _pythonQtClassWrapper = NULL; if (wrappedClassName.isEmpty()) { _metaTypeId = -1; } else { diff --git a/src/PythonQtClassInfo.h b/src/PythonQtClassInfo.h index 5f7392d..caee554 100644 --- a/src/PythonQtClassInfo.h +++ b/src/PythonQtClassInfo.h @@ -125,6 +125,12 @@ public: //! check if the special method "hasOwner" is implemented and if it returns false, which means that the object may be destroyed bool hasOwnerMethodButNoOwner(void* object); + //! set the associated PythonQtClassWrapper (which handles instance creation of this type) + void setPythonQtClassWrapper(PyObject* obj) { _pythonQtClassWrapper = obj; } + + //! get the associated PythonQtClassWrapper (which handles instance creation of this type) + PyObject* pythonQtClassWrapper() { return _pythonQtClassWrapper; } + private: //! resolve the parent class from either meta object or cpp parent class name void resolveParentClassInfo(); @@ -150,6 +156,8 @@ private: PythonQtQObjectCreatorFunctionCB* _decoratorProviderCB; PythonQtClassInfo* _parentClassInfo; + PyObject* _pythonQtClassWrapper; + bool _parentClassInfoResolved; int _metaTypeId; diff --git a/src/PythonQtClassWrapper.cpp b/src/PythonQtClassWrapper.cpp index b477a3d..8dd46d5 100644 --- a/src/PythonQtClassWrapper.cpp +++ b/src/PythonQtClassWrapper.cpp @@ -47,72 +47,50 @@ #include "PythonQtClassInfo.h" #include "PythonQtConversion.h" -static void PythonQtClassWrapper_dealloc(PythonQtClassWrapper* self) +static PyObject* PythonQtClassWrapper_alloc(PyTypeObject *self, Py_ssize_t nitems) { - self->ob_type->tp_free((PyObject*)self); -} + // call the default type alloc + PyObject* obj = PyType_Type.tp_alloc(self, nitems); -static PyObject* PythonQtClassWrapper_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/) -{ - PythonQtClassWrapper *self; + // take current class type, if we are called via newPythonQtClassWrapper() + PythonQtClassWrapper* wrap = (PythonQtClassWrapper*)obj; + wrap->_classInfo = PythonQt::priv()->currentClassInfoForClassWrapperCreation(); - self = (PythonQtClassWrapper *)type->tp_alloc(type, 0); - if (self != NULL) { - self->_info = NULL; - } - return (PyObject *)self; + return obj; } -static int PythonQtClassWrapper_init(PythonQtClassWrapper * /*self*/, PyObject * /*args*/, PyObject * /*kwds*/) -{ - return 0; -} -PyObject *PythonQtClassWrapper_call(PyObject *func, PyObject *args, PyObject *kw) { - PythonQtClassWrapper* wrapper = (PythonQtClassWrapper*)func; - PyObject* result = NULL; - QString error; - PyObject* err = NULL; - if (wrapper->_info->constructors()) { - result = PythonQtSlotFunction_CallImpl(NULL, wrapper->_info->constructors(), args, kw); - err = PyErr_Occurred(); - } - if (!result) { - QObject* v = NULL; - QListIterator it(PythonQt::self()->constructorHandlers()); - while (!v && it.hasNext()) { - v = it.next()->create(wrapper->_info->metaObject(), args, kw, error); - } - if (v) { - result = PythonQt::priv()->wrapQObject(v); - } +static int PythonQtClassWrapper_init(PythonQtClassWrapper* self, PyObject* args, PyObject* kwds) +{ + // call the default type init + if (PyType_Type.tp_init((PyObject *)self, args, kwds) < 0) { + return -1; } - if (result) { - // change ownershipflag to be owned by PythonQt - if (result->ob_type == &PythonQtInstanceWrapper_Type) { - ((PythonQtInstanceWrapper*)result)->_ownedByPythonQt = true; - } - } else { - if (!wrapper->_info->constructors()) { - if (!err) { - if (error.isEmpty()) { - error = QString("No constructors available for ") + wrapper->_info->className(); - } - PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); - } + + // if we have no CPP class information, try our base class + if (!self->classInfo()) { + PyTypeObject* superType = ((PyTypeObject *)self)->tp_base; + + if (!superType || (superType->ob_type != &PythonQtClassWrapper_Type)) { + PyErr_Format(PyExc_TypeError, "type %s is not derived from PythonQtClassWrapper", ((PyTypeObject*)self)->tp_name); + return -1; } + + // take the class info from the superType + self->_classInfo = ((PythonQtClassWrapper*)superType)->classInfo(); } - return result; + + return 0; } static PyObject *PythonQtClassWrapper_classname(PythonQtClassWrapper* type) { - return PyString_FromString((QString("Meta_") + type->_info->className()).toLatin1().data()); + return PyString_FromString((QString("Class_") + type->classInfo()->className()).toLatin1().data()); } static PyObject *PythonQtClassWrapper_help(PythonQtClassWrapper* type) { - return PythonQt::self()->helpCalled(type->_info); + return PythonQt::self()->helpCalled(type->classInfo()); } @@ -127,7 +105,7 @@ static PyMethodDef PythonQtClassWrapper_methods[] = { }; -static PyObject *PythonQtClassWrapper_getattro(PyObject *obj,PyObject *name) +static PyObject *PythonQtClassWrapper_getattro(PyObject *obj, PyObject *name) { const char *attributeName; PythonQtClassWrapper *wrapper = (PythonQtClassWrapper *)obj; @@ -136,7 +114,7 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj,PyObject *name) return NULL; } - PythonQtMemberInfo member = wrapper->_info->member(attributeName); + PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName); if (member._type == PythonQtMemberInfo::EnumValue) { return PyInt_FromLong(member._enumValue); } @@ -152,7 +130,7 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj,PyObject *name) PyErr_Clear(); if (qstrcmp(attributeName, "__dict__")==0) { - QStringList l = wrapper->_info->memberList(true); + QStringList l = wrapper->classInfo()->memberList(true); PyObject* dict = PyDict_New(); foreach (QString name, l) { //PyObject* o = PyObject_GetAttrString(obj, name.toLatin1().data()); @@ -162,53 +140,47 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj,PyObject *name) return dict; } - QString error = QString(wrapper->_info->className()) + " has no attribute named '" + QString(attributeName) + "'"; + QString error = QString(wrapper->classInfo()->className()) + " has no attribute named '" + QString(attributeName) + "'"; PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); return NULL; } +static int PythonQtClassWrapper_setattro(PyObject *obj,PyObject *name,PyObject * /*value*/) +{ + QString error; + char *attributeName; + if ((attributeName = PyString_AsString(name)) == NULL) { + return -1; + } + PythonQtClassWrapper *wrapper = (PythonQtClassWrapper *)obj; + + // TODO + return -1; +} + +/* static PyObject * PythonQtClassWrapper_repr(PyObject * obj) { PythonQtClassWrapper* wrapper = (PythonQtClassWrapper*)obj; - if (wrapper->_info->isCPPWrapper()) { - const QMetaObject* meta = wrapper->_info->metaObject(); + if (wrapper->classInfo()->isCPPWrapper()) { + const QMetaObject* meta = wrapper->classInfo()->metaObject(); if (!meta) { - QObject* decorator = wrapper->_info->decorator(); + QObject* decorator = wrapper->classInfo()->decorator(); if (decorator) { meta = decorator->metaObject(); } } if (meta) { - return PyString_FromFormat("%s Class (C++ wrapped by %s)", wrapper->_info->className(), meta->className()); - } else { - return PyString_FromFormat("%s Class (C++ unwrapped)", wrapper->_info->className()); - } - } else { - return PyString_FromFormat("%s Class", wrapper->_info->className()); - } -} - -static int PythonQtClassWrapper_compare(PyObject * obj1, PyObject * obj2) -{ - if (obj1->ob_type == &PythonQtClassWrapper_Type && - obj2->ob_type == &PythonQtClassWrapper_Type) { - - PythonQtClassWrapper* w1 = (PythonQtClassWrapper*)obj1; - PythonQtClassWrapper* w2 = (PythonQtClassWrapper*)obj2; - if (w1->_info == w2->_info) { - return 0; + return PyString_FromFormat("%s Class (C++ wrapped by %s)", wrapper->classInfo()->className(), meta->className()); } else { - return -1; + return PyString_FromFormat("%s Class (C++ unwrapped)", wrapper->classInfo()->className()); } } else { - return -1; + return PyString_FromFormat("%s Class", wrapper->classInfo()->className()); } } -static long PythonQtClassWrapper_hash(PythonQtClassWrapper *obj) -{ - return reinterpret_cast(obj->_info); -} +*/ PyTypeObject PythonQtClassWrapper_Type = { PyObject_HEAD_INIT(NULL) @@ -216,23 +188,23 @@ PyTypeObject PythonQtClassWrapper_Type = { "PythonQt.PythonQtClassWrapper", /*tp_name*/ sizeof(PythonQtClassWrapper), /*tp_basicsize*/ 0, /*tp_itemsize*/ - (destructor)PythonQtClassWrapper_dealloc, /*tp_dealloc*/ + 0, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ - PythonQtClassWrapper_compare, /*tp_compare*/ - PythonQtClassWrapper_repr, /*tp_repr*/ + 0, /*tp_compare*/ + 0, //PythonQtClassWrapper_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ - (hashfunc)PythonQtClassWrapper_hash, /*tp_hash */ - PythonQtClassWrapper_call, /*tp_call*/ + 0, /*tp_hash */ + 0, /*tp_call*/ 0, /*tp_str*/ PythonQtClassWrapper_getattro, /*tp_getattro*/ - 0, /*tp_setattro*/ + PythonQtClassWrapper_setattro, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "PythonQtClassWrapper object", /* tp_doc */ + 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -248,8 +220,9 @@ PyTypeObject PythonQtClassWrapper_Type = { 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)PythonQtClassWrapper_init, /* tp_init */ - 0, /* tp_alloc */ - PythonQtClassWrapper_new, /* tp_new */ + PythonQtClassWrapper_alloc, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ }; //------------------------------------------------------- diff --git a/src/PythonQtClassWrapper.h b/src/PythonQtClassWrapper.h index 9bd82eb..437807d 100644 --- a/src/PythonQtClassWrapper.h +++ b/src/PythonQtClassWrapper.h @@ -51,29 +51,25 @@ #include class PythonQtClassInfo; -class QObject; -struct QMetaObject; +//! the type of the PythonQt class wrapper objects extern PyTypeObject PythonQtClassWrapper_Type; //--------------------------------------------------------------- -//! a Python wrapper object for Qt meta objects +//! a Python wrapper object for PythonQt wrapped classes +//! which inherits from the Python type object to allow +//! deriving of wrapped CPP classes from Python. typedef struct { - PyObject_HEAD + PyHeapTypeObject _base; - //! the class information (which contains the meta object as well) - PythonQtClassInfo* _info; + //! the additional class information that PythonQt stores for the CPP class + PythonQtClassInfo* _classInfo; + + //! get the class info + PythonQtClassInfo* classInfo() { return _classInfo; } } PythonQtClassWrapper; //--------------------------------------------------------------- -// an abstact class for handling construction of objects -class PythonQtConstructorHandler { -public: - //! get rid of warnings - virtual ~PythonQtConstructorHandler() {} - - virtual QObject* create(const QMetaObject* meta, PyObject *args, PyObject *kw, QString& error) = 0; -}; -#endif \ No newline at end of file +#endif diff --git a/src/PythonQtConversion.cpp b/src/PythonQtConversion.cpp index 61472c4..38edc30 100644 --- a/src/PythonQtConversion.cpp +++ b/src/PythonQtConversion.cpp @@ -224,15 +224,15 @@ return Py_None; { bool ok; void* ptr = NULL; - if (obj->ob_type == &PythonQtInstanceWrapper_Type && info.typeId != PythonQtMethodInfo::Variant) { + if (PyObject_TypeCheck(obj, &PythonQtInstanceWrapper_Type) && info.typeId != PythonQtMethodInfo::Variant) { // if we have a Qt wrapper object and if we do not need a QVariant, we do the following: // (the Variant case is handled below in a switch) // a C++ wrapper (can be passed as pointer or reference) PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)obj; - if (wrap->_info->inherits(info.name)) { + if (wrap->classInfo()->inherits(info.name)) { void* object; - if (wrap->_info->isCPPWrapper()) { + if (wrap->classInfo()->isCPPWrapper()) { object = wrap->_wrappedPtr; } else { QObject* tmp = wrap->_obj; @@ -690,13 +690,13 @@ QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type) type = QVariant::Double; } else if (val == Py_False || val == Py_True) { type = QVariant::Bool; - } else if (val->ob_type == &PythonQtInstanceWrapper_Type) { + } else if (PyObject_TypeCheck(val, &PythonQtInstanceWrapper_Type)) { PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)val; // c++ wrapper, check if the class names of the c++ objects match - if (wrap->_info->isCPPWrapper()) { - if (wrap->_info->metaTypeId()>0) { + if (wrap->classInfo()->isCPPWrapper()) { + if (wrap->classInfo()->metaTypeId()>0) { // construct a new variant from the C++ object if it has a meta type - v = QVariant(wrap->_info->metaTypeId(), wrap->_wrappedPtr); + v = QVariant(wrap->classInfo()->metaTypeId(), wrap->_wrappedPtr); } else { // is this worth anything? we loose the knowledge of the cpp object type v = qVariantFromValue(wrap->_wrappedPtr); @@ -849,9 +849,9 @@ QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type) break; default: - if (val->ob_type == &PythonQtInstanceWrapper_Type) { + if (PyObject_TypeCheck(val, &PythonQtInstanceWrapper_Type)) { PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)val; - if (wrap->_info->isCPPWrapper() && wrap->_info->metaTypeId() == type) { + if (wrap->classInfo()->isCPPWrapper() && wrap->classInfo()->metaTypeId() == type) { // construct a new variant from the C++ object if it has the same meta type v = QVariant(type, wrap->_wrappedPtr); } else { @@ -956,11 +956,11 @@ bool PythonQtConv::ConvertPythonListToQListOfPointerType(PyObject* obj, QListob_type == &PythonQtInstanceWrapper_Type) { + if (PyObject_TypeCheck(value, &PythonQtInstanceWrapper_Type)) { PythonQtInstanceWrapper* wrap = (PythonQtInstanceWrapper*)value; // both QObjects and CPP wrappers support inherits, so we use that to check of we match - if (wrap->_info->inherits(type)) { - if (wrap->_info->isCPPWrapper()) { + if (wrap->classInfo()->inherits(type)) { + if (wrap->classInfo()->isCPPWrapper()) { list->append(wrap->_wrappedPtr); } else { QObject* myObject = wrap->_obj; diff --git a/src/PythonQtInstanceWrapper.cpp b/src/PythonQtInstanceWrapper.cpp index 334fe26..1af948c 100644 --- a/src/PythonQtInstanceWrapper.cpp +++ b/src/PythonQtInstanceWrapper.cpp @@ -45,24 +45,31 @@ #include "PythonQtSlot.h" #include "PythonQtClassInfo.h" #include "PythonQtConversion.h" +#include "PythonQtClassWrapper.h" + +PythonQtClassInfo* PythonQtInstanceWrapper::classInfo() +{ + // take the class info from our type object + return ((PythonQtClassWrapper*)ob_type)->_classInfo; +} static void PythonQtInstanceWrapper_deleteObject(PythonQtInstanceWrapper* self, bool force = false) { // is this a C++ wrapper? if (self->_wrappedPtr) { - //mlabDebugConst("Python","c++ wrapper removed " << self->_wrappedPtr << " " << self->_obj->className() << " " << self->_info->wrappedClassName().latin1()); + //mlabDebugConst("Python","c++ wrapper removed " << self->_wrappedPtr << " " << self->_obj->className() << " " << self->classInfo()->wrappedClassName().latin1()); PythonQt::priv()->removeWrapperPointer(self->_wrappedPtr); // we own our qobject, so we delete it now: delete self->_obj; self->_obj = NULL; - if (force || self->_info->hasOwnerMethodButNoOwner(self->_wrappedPtr) || self->_ownedByPythonQt) { - int type = self->_info->metaTypeId(); + if (force || self->classInfo()->hasOwnerMethodButNoOwner(self->_wrappedPtr) || self->_ownedByPythonQt) { + int type = self->classInfo()->metaTypeId(); if (self->_useQMetaTypeDestroy && type>=0) { // use QMetaType to destroy the object QMetaType::destroy(type, self->_wrappedPtr); } else { - PythonQtSlotInfo* slot = PythonQt::priv()->getDestructorSlot(self->_info->className()); + PythonQtSlotInfo* slot = PythonQt::priv()->getDestructorSlot(self->classInfo()->className()); if (slot) { void* args[2]; args[0] = NULL; @@ -80,7 +87,7 @@ static void PythonQtInstanceWrapper_deleteObject(PythonQtInstanceWrapper* self, } } } else { - //mlabDebugConst("Python","qobject wrapper removed " << self->_obj->className() << " " << self->_info->wrappedClassName().latin1()); + //mlabDebugConst("Python","qobject wrapper removed " << self->_obj->className() << " " << self->classInfo()->wrappedClassName().latin1()); if (self->_objPointerCopy) { PythonQt::priv()->removeWrapperPointer(self->_objPointerCopy); } @@ -107,13 +114,18 @@ static void PythonQtInstanceWrapper_dealloc(PythonQtInstanceWrapper* self) self->ob_type->tp_free((PyObject*)self); } -static PyObject* PythonQtInstanceWrapper_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/) +static PyObject* PythonQtInstanceWrapper_new(PyTypeObject *type, PyObject * args, PyObject * /*kwds*/) { + PythonQtClassWrapper *classType = (PythonQtClassWrapper*)type; PythonQtInstanceWrapper *self; + static PyObject* emptyTuple = NULL; + if (emptyTuple==NULL) { + emptyTuple = PyTuple_New(0); + } + + self = (PythonQtInstanceWrapper*)PyBaseObject_Type.tp_new(type, emptyTuple, NULL); - self = (PythonQtInstanceWrapper *)type->tp_alloc(type, 0); if (self != NULL) { - self->_info = NULL; new (&self->_obj) QPointer(); self->_wrappedPtr = NULL; self->_ownedByPythonQt = false; @@ -122,19 +134,51 @@ static PyObject* PythonQtInstanceWrapper_new(PyTypeObject *type, PyObject * /*ar return (PyObject *)self; } -static int PythonQtInstanceWrapper_init(PythonQtInstanceWrapper * /*self*/, PyObject * /*args*/, PyObject * /*kwds*/) +static int PythonQtInstanceWrapper_init(PythonQtInstanceWrapper * self, PyObject * args, PyObject * kwds) { + PyObject* result = NULL; + + if (args == PythonQtPrivate::dummyTuple()) { + // we are called from the internal PythonQt API, so our data will be filled later on... + return 0; + } + + // we are called from python, try to construct our object + if (self->classInfo()->constructors()) { + void* directCPPPointer = NULL; + PythonQtSlotFunction_CallImpl(NULL, self->classInfo()->constructors(), args, kwds, NULL, &directCPPPointer); + if (PyErr_Occurred()) { + return -1; + } + if (directCPPPointer) { + // change ownershipflag to be owned by PythonQt + self->_ownedByPythonQt = true; + self->_useQMetaTypeDestroy = false; + if (self->classInfo()->isCPPWrapper()) { + self->_wrappedPtr = directCPPPointer; + // TODO xxx: if there is a wrapper factory, we might want to generate a wrapper for our class?! + } else { + self->setQObject((QObject*)directCPPPointer); + } + // register with PythonQt + PythonQt::priv()->addWrapperPointer(directCPPPointer, self); + } + } else { + QString error = QString("No constructors available for ") + self->classInfo()->className(); + PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); + return -1; + } return 0; } static PyObject *PythonQtInstanceWrapper_classname(PythonQtInstanceWrapper* type) { - return PyString_FromString(type->_info->className()); + return PyString_FromString(type->classInfo()->className()); } static PyObject *PythonQtInstanceWrapper_help(PythonQtInstanceWrapper* type) { - return PythonQt::self()->helpCalled(type->_info); + return PythonQt::self()->helpCalled(type->classInfo()); } static PyObject *PythonQtInstanceWrapper_delete(PythonQtInstanceWrapper * self) @@ -169,7 +213,7 @@ static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name) } if (!wrapper->_obj && !wrapper->_wrappedPtr) { - QString error = QString("Trying to read attribute '") + attributeName + "' from a destroyed " + wrapper->_info->className() + " object"; + QString error = QString("Trying to read attribute '") + attributeName + "' from a destroyed " + wrapper->classInfo()->className() + " object"; PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); return NULL; } @@ -178,7 +222,7 @@ static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name) // TODO: dynamic properties are missing - PythonQtMemberInfo member = wrapper->_info->member(attributeName); + PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName); switch (member._type) { case PythonQtMemberInfo::Property: if (wrapper->_obj) { @@ -215,7 +259,7 @@ static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name) } if (qstrcmp(attributeName, "__dict__")==0) { - QStringList l = wrapper->_info->memberList(false); + QStringList l = wrapper->classInfo()->memberList(false); PyObject* dict = PyDict_New(); foreach (QString name, l) { //PyObject* o = PyObject_GetAttrString(obj, name.toLatin1().data()); @@ -227,7 +271,7 @@ static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name) } - QString error = QString(wrapper->_info->className()) + " has no attribute named '" + QString(attributeName) + "'"; + QString error = QString(wrapper->classInfo()->className()) + " has no attribute named '" + QString(attributeName) + "'"; PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); return NULL; } @@ -242,12 +286,12 @@ static int PythonQtInstanceWrapper_setattro(PyObject *obj,PyObject *name,PyObjec return -1; if (!wrapper->_obj) { - error = QString("Trying to set attribute '") + attributeName + "' on a destroyed " + wrapper->_info->className() + " object"; + error = QString("Trying to set attribute '") + attributeName + "' on a destroyed " + wrapper->classInfo()->className() + " object"; PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); return -1; } - PythonQtMemberInfo member = wrapper->_info->member(attributeName); + PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName); if (member._type == PythonQtMemberInfo::Property) { QMetaProperty prop = member._property; if (prop.isWritable()) { @@ -271,13 +315,13 @@ static int PythonQtInstanceWrapper_setattro(PyObject *obj,PyObject *name,PyObjec + QString(value->ob_type->tp_name) + " (" + PythonQtConv::PyObjGetRepresentation(value) + ")"; } } else { - error = QString("Property '") + attributeName + "' of " + wrapper->_info->className() + " object is not writable"; + error = QString("Property '") + attributeName + "' of " + wrapper->classInfo()->className() + " object is not writable"; } } else { if (member._type == PythonQtMemberInfo::Slot) { - error = QString("Slot '") + attributeName + "' can not be overwritten on " + wrapper->_info->className() + " object"; + error = QString("Slot '") + attributeName + "' can not be overwritten on " + wrapper->classInfo()->className() + " object"; } else if (member._type == PythonQtMemberInfo::EnumValue) { - error = QString("EnumValue '") + attributeName + "' can not be overwritten on " + wrapper->_info->className() + " object"; + error = QString("EnumValue '") + attributeName + "' can not be overwritten on " + wrapper->classInfo()->className() + " object"; } } @@ -290,17 +334,17 @@ static PyObject * PythonQtInstanceWrapper_str(PyObject * obj) PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)obj; QObject *qobj = wrapper->_obj; if (wrapper->_wrappedPtr) { - QString str = PythonQtConv::CPPObjectToString(wrapper->_info->metaTypeId(), wrapper->_wrappedPtr); + QString str = PythonQtConv::CPPObjectToString(wrapper->classInfo()->metaTypeId(), wrapper->_wrappedPtr); if (!str.isEmpty()) { return PyString_FromFormat("%s", str.toLatin1().constData()); } else if (wrapper->_obj) { - return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", wrapper->_info->className(), wrapper->_wrappedPtr, wrapper->_obj->metaObject()->className(), qobj); + return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", wrapper->classInfo()->className(), wrapper->_wrappedPtr, wrapper->_obj->metaObject()->className(), qobj); } else { - return PyString_FromFormat("%s (C++ Object %p)", wrapper->_info->className(), wrapper->_wrappedPtr); + return PyString_FromFormat("%s (C++ Object %p)", wrapper->classInfo()->className(), wrapper->_wrappedPtr); } } else { - return PyString_FromFormat("%s (QObject %p)", wrapper->_info->className(), qobj); + return PyString_FromFormat("%s (QObject %p)", wrapper->classInfo()->className(), qobj); } } @@ -309,24 +353,24 @@ static PyObject * PythonQtInstanceWrapper_repr(PyObject * obj) PythonQtInstanceWrapper* wrapper = (PythonQtInstanceWrapper*)obj; QObject *qobj = wrapper->_obj; if (wrapper->_wrappedPtr) { - QString str = PythonQtConv::CPPObjectToString(wrapper->_info->metaTypeId(), wrapper->_wrappedPtr); + QString str = PythonQtConv::CPPObjectToString(wrapper->classInfo()->metaTypeId(), wrapper->_wrappedPtr); if (!str.isEmpty()) { - return PyString_FromFormat("%s(%s, %p)", QMetaType::typeName(wrapper->_info->metaTypeId()), str.toLatin1().constData(), wrapper->_wrappedPtr); + return PyString_FromFormat("%s(%s, %p)", QMetaType::typeName(wrapper->classInfo()->metaTypeId()), str.toLatin1().constData(), wrapper->_wrappedPtr); } else if (wrapper->_obj) { - return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", wrapper->_info->className(), wrapper->_wrappedPtr, wrapper->_obj->metaObject()->className(), qobj); + return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", wrapper->classInfo()->className(), wrapper->_wrappedPtr, wrapper->_obj->metaObject()->className(), qobj); } else { - return PyString_FromFormat("%s (C++ Object %p)", wrapper->_info->className(), wrapper->_wrappedPtr); + return PyString_FromFormat("%s (C++ Object %p)", wrapper->classInfo()->className(), wrapper->_wrappedPtr); } } else { - return PyString_FromFormat("%s (QObject %p)", wrapper->_info->className(), qobj); + return PyString_FromFormat("%s (QObject %p)", wrapper->classInfo()->className(), qobj); } } static int PythonQtInstanceWrapper_compare(PyObject * obj1, PyObject * obj2) { - if (obj1->ob_type == &PythonQtInstanceWrapper_Type && - obj2->ob_type == &PythonQtInstanceWrapper_Type) { + if (PyObject_TypeCheck(obj1, &PythonQtInstanceWrapper_Type) && + PyObject_TypeCheck(obj2, &PythonQtInstanceWrapper_Type)) { PythonQtInstanceWrapper* w1 = (PythonQtInstanceWrapper*)obj1; PythonQtInstanceWrapper* w2 = (PythonQtInstanceWrapper*)obj2; @@ -338,11 +382,11 @@ static int PythonQtInstanceWrapper_compare(PyObject * obj1, PyObject * obj2) } else if (w1->_obj == w2->_obj) { return 0; } - const char* class1 = w1->_info->className(); - const char* class2 = w2->_info->className(); + const char* class1 = w1->classInfo()->className(); + const char* class2 = w2->classInfo()->className(); if (strcmp(class1, class2) == 0) { // same class names, so we can try the operator_equal - PythonQtMemberInfo info = w1->_info->member("operator_equal"); + PythonQtMemberInfo info = w1->classInfo()->member("operator_equal"); if (info._type == PythonQtMemberInfo::Slot) { bool result = false; void* obj1 = w1->_wrappedPtr; @@ -440,7 +484,7 @@ static PyNumberMethods PythonQtInstanceWrapper_as_number = { }; PyTypeObject PythonQtInstanceWrapper_Type = { - PyObject_HEAD_INIT(NULL) + PyObject_HEAD_INIT(&PythonQtClassWrapper_Type) 0, /*ob_size*/ "PythonQt.PythonQtInstanceWrapper", /*tp_name*/ sizeof(PythonQtInstanceWrapper), /*tp_basicsize*/ diff --git a/src/PythonQtInstanceWrapper.h b/src/PythonQtInstanceWrapper.h index 1fe691c..f41fa12 100644 --- a/src/PythonQtInstanceWrapper.h +++ b/src/PythonQtInstanceWrapper.h @@ -62,6 +62,9 @@ extern PYTHONQT_EXPORT PyTypeObject PythonQtInstanceWrapper_Type; typedef struct { PyObject_HEAD + //! the class information, this is set even if the _obj or _wrappedPtr is NULL to support typed NULL pointers + PythonQtClassInfo* classInfo(); + //! set the QObject pointer void setQObject(QObject* object) { _obj = object; @@ -77,9 +80,6 @@ typedef struct { //! optional C++ object Ptr that is wrapped by the above _obj void* _wrappedPtr; - //! the class information, this is set even if the _obj or _wrappedPtr is NULL to support typed NULL pointers - PythonQtClassInfo* _info; - //! flag that stores if the object is owned by pythonQt bool _ownedByPythonQt; diff --git a/src/PythonQtSlot.cpp b/src/PythonQtSlot.cpp index 2f30039..ff374e5 100644 --- a/src/PythonQtSlot.cpp +++ b/src/PythonQtSlot.cpp @@ -50,10 +50,13 @@ #define PYTHONQT_MAX_ARGS 32 -PyObject* PythonQtCallSlot(QObject* objectToCall, PyObject* args, bool strict, PythonQtSlotInfo* info, void* firstArgument) +bool PythonQtCallSlot(QObject* objectToCall, PyObject* args, bool strict, PythonQtSlotInfo* info, void* firstArgument, PyObject** pythonReturnValue, void** directReturnValuePointer) { static unsigned int recursiveEntry = 0; + if (directReturnValuePointer) { + *directReturnValuePointer = NULL; + } // store the current storage position, so that we can get back to this state after a slot is called // (do this locally, so that we have all positions on the stack PythonQtValueStoragePosition globalValueStoragePos; @@ -87,7 +90,13 @@ PyObject* PythonQtCallSlot(QObject* objectToCall, PyObject* args, bool strict, P } if (argList[0]==NULL) { // create empty default value for the return value - argList[0] = PythonQtConv::CreateQtReturnValue(returnValueParam); + if (!directReturnValuePointer) { + // create empty default value for the return value + argList[0] = PythonQtConv::CreateQtReturnValue(returnValueParam); + } else { + // we can use our pointer directly! + argList[0] = directReturnValuePointer; + } } } @@ -129,10 +138,14 @@ PyObject* PythonQtCallSlot(QObject* objectToCall, PyObject* args, bool strict, P (info->decorator()?info->decorator():objectToCall)->qt_metacall(QMetaObject::InvokeMetaMethod, info->slotIndex(), argList); if (argList[0] || returnValueParam.typeId == QMetaType::Void) { - if (!returnValueIsEnum) { - result = PythonQtConv::ConvertQtValueToPython(returnValueParam, argList[0]); + if (directReturnValuePointer) { + result = NULL; } else { - result = PyInt_FromLong(*((unsigned int*)argList[0])); + if (!returnValueIsEnum) { + result = PythonQtConv::ConvertQtValueToPython(returnValueParam, argList[0]); + } else { + result = PyInt_FromLong(*((unsigned int*)argList[0])); + } } } else { QString e = QString("Called ") + info->fullSignature(skipFirst) + ", return type is ignored because it is unknown to PythonQt."; @@ -147,8 +160,9 @@ PyObject* PythonQtCallSlot(QObject* objectToCall, PyObject* args, bool strict, P PythonQtConv::global_ptrStorage.setPos(globalPtrStoragePos); PythonQtConv::global_variantStorage.setPos(globalVariantStoragePos); + *pythonReturnValue = result; // NOTE: it is important to only return here, otherwise the stack will not be popped!!! - return result; + return result || (directReturnValuePointer && *directReturnValuePointer); } //----------------------------------------------------------------------------------- @@ -159,7 +173,7 @@ PyObject *PythonQtSlotFunction_Call(PyObject *func, PyObject *args, PyObject *kw { PythonQtSlotFunctionObject* f = (PythonQtSlotFunctionObject*)func; PythonQtSlotInfo* info = f->m_ml; - if (f->m_self->ob_type == &PythonQtInstanceWrapper_Type) { + if (PyObject_TypeCheck(f->m_self, &PythonQtInstanceWrapper_Type)) { PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*) f->m_self; return PythonQtSlotFunction_CallImpl(self->_obj, info, args, kw, self->_wrappedPtr); } else if (f->m_self->ob_type == &PythonQtClassWrapper_Type) { @@ -169,7 +183,7 @@ PyObject *PythonQtSlotFunction_Call(PyObject *func, PyObject *args, PyObject *kw } } -PyObject *PythonQtSlotFunction_CallImpl(QObject* objectToCall, PythonQtSlotInfo* info, PyObject *args, PyObject * /*kw*/, void* firstArg) +PyObject *PythonQtSlotFunction_CallImpl(QObject* objectToCall, PythonQtSlotInfo* info, PyObject *args, PyObject * /*kw*/, void* firstArg, void** directReturnValuePointer) { int argc = PyTuple_Size(args); @@ -178,33 +192,32 @@ PyObject *PythonQtSlotFunction_CallImpl(QObject* objectToCall, PythonQtSlotInfo* #endif PyObject* r = NULL; - + bool ok = false; + if (directReturnValuePointer) { + *directReturnValuePointer = NULL; + } + if (info->nextInfo()) { // overloaded slot call, try on all slots with strict conversion first + bool strict = true; PythonQtSlotInfo* i = info; - while (i && r==NULL) { + while (i) { bool skipFirst = i->isInstanceDecorator(); if (i->parameterCount()-1-(skipFirst?1:0) == argc) { PyErr_Clear(); - r = PythonQtCallSlot(objectToCall, args, true, i, firstArg); - if (PyErr_Occurred()) break; + ok = PythonQtCallSlot(objectToCall, args, strict, i, firstArg, &r, directReturnValuePointer); + if (PyErr_Occurred() || ok) break; } i = i->nextInfo(); - } - if (!r) { - // try on all slots with non-strict conversion - i = info; - while (i && r==NULL) { - bool skipFirst = i->isInstanceDecorator(); - if (i->parameterCount()-1-(skipFirst?1:0) == argc) { - PyErr_Clear(); - r = PythonQtCallSlot(objectToCall, args, false, i, firstArg); - if (PyErr_Occurred()) break; + if (!i) { + if (strict) { + // one more run without being strict + strict = false; + i = info; } - i = i->nextInfo(); } } - if (r==NULL && !PyErr_Occurred()) { + if (!ok && !PyErr_Occurred()) { QString e = QString("Could not find matching overload for given arguments:\n" + PythonQtConv::PyObjGetString(args) + "\n The following slots are available:\n"); PythonQtSlotInfo* i = info; while (i) { @@ -219,8 +232,8 @@ PyObject *PythonQtSlotFunction_CallImpl(QObject* objectToCall, PythonQtSlotInfo* bool skipFirst = info->isInstanceDecorator(); if (info->parameterCount()-1-(skipFirst?1:0) == argc) { PyErr_Clear(); - r = PythonQtCallSlot(objectToCall, args, false, info, firstArg); - if (r==NULL && !PyErr_Occurred()) { + ok = PythonQtCallSlot(objectToCall, args, false, info, firstArg, &r, directReturnValuePointer); + if (!ok && !PyErr_Occurred()) { QString e = QString("Called ") + info->fullSignature(skipFirst) + " with wrong arguments: " + PythonQtConv::PyObjGetString(args); PyErr_SetString(PyExc_ValueError, e.toLatin1().data()); } diff --git a/src/PythonQtSlot.h b/src/PythonQtSlot.h index 5f8539f..b19400e 100644 --- a/src/PythonQtSlot.h +++ b/src/PythonQtSlot.h @@ -61,7 +61,7 @@ PyObject* PythonQtSlotFunction_GetSelf(PyObject *); PyObject* PythonQtSlotFunction_Call(PyObject *, PyObject *, PyObject *); -PyObject *PythonQtSlotFunction_CallImpl(QObject* objectToCall, PythonQtSlotInfo* info, PyObject *args, PyObject *kw, void* firstArg=NULL); +PyObject *PythonQtSlotFunction_CallImpl(QObject* objectToCall, PythonQtSlotInfo* info, PyObject *args, PyObject *kw, void* firstArg=NULL, void** directReturnValuePointer=NULL); PyObject* PythonQtSlotFunction_New(PythonQtSlotInfo *, PyObject *,