diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index 743161b..2529576 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -52,6 +52,7 @@ #include PythonQt* PythonQt::_self = NULL; +int PythonQt::_uniqueModuleCount = 0; void PythonQt::init(int flags) @@ -211,6 +212,13 @@ void PythonQt::registerClass(const QMetaObject* metaobject) _p->registerClass(metaobject); } +void PythonQt::qObjectNoLongerWrappedCB(QObject* o) +{ + if (_self->_p->_noLongerWrappedCB) { + (*_self->_p->_noLongerWrappedCB)(o); + }; +} + void PythonQtPrivate::registerClass(const QMetaObject* metaobject) { // we register all classes in the hierarchy @@ -259,7 +267,7 @@ PyObject* PythonQtPrivate::wrapQObject(QObject* obj) Py_INCREF(Py_None); return Py_None; } - PythonQtWrapper* wrap = _wrappedObjects.value(obj); + PythonQtWrapper* wrap = findWrapperAndRemoveUnused(obj); if (!wrap) { // smuggling it in... PythonQtClassInfo* classInfo = _knownQtClasses.value(obj->metaObject()->className()); @@ -268,8 +276,6 @@ PyObject* PythonQtPrivate::wrapQObject(QObject* obj) classInfo = _knownQtClasses.value(obj->metaObject()->className()); } wrap = createNewPythonQtWrapper(obj, classInfo); - // insert destroyed handler - connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(wrappedObjectDestroyed(QObject*))); // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1()); } else { Py_INCREF(wrap); @@ -284,7 +290,7 @@ PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name) Py_INCREF(Py_None); return Py_None; } - PythonQtWrapper* wrap = _wrappedObjects.value(ptr); + PythonQtWrapper* wrap = findWrapperAndRemoveUnused(ptr); if (!wrap) { PythonQtClassInfo* info = _knownQtClasses.value(name); if (!info) { @@ -304,8 +310,6 @@ PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name) info = _knownQtClasses.value(qptr->metaObject()->className()); } wrap = createNewPythonQtWrapper(qptr, info); - // insert destroyed handler - connect(qptr, SIGNAL(destroyed(QObject*)), this, SLOT(wrappedObjectDestroyed(QObject*))); // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->_info->wrappedClassName().latin1()); } else { // maybe it is a PyObject, which we can return directly @@ -360,7 +364,7 @@ PythonQtWrapper* PythonQtPrivate::createNewPythonQtWrapper(QObject* obj, PythonQ result = (PythonQtWrapper *)PythonQtWrapper_Type.tp_new(&PythonQtWrapper_Type, NULL, NULL); - result->_obj = obj; + result->setQObject(obj); result->_info = info; result->_wrappedPtr = wrappedPtr; result->_ownedByPythonQt = false; @@ -369,6 +373,10 @@ PythonQtWrapper* PythonQtPrivate::createNewPythonQtWrapper(QObject* obj, PythonQ _wrappedObjects.insert(wrappedPtr, result); } else { _wrappedObjects.insert(obj, result); + if (obj->parent()== NULL && _wrappedCB) { + // tell someone who is interested that the qobject is wrapped the first time, if it has no parent + (*_wrappedCB)(obj); + } } return result; } @@ -400,8 +408,6 @@ PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj) if (!r) { r = new PythonQtSignalReceiver(obj); _p->_signalReceivers.insert(obj, r); - // insert destroyed handler - connect(obj, SIGNAL(destroyed(QObject*)), _p ,SLOT(destroyedSignalEmitter(QObject*))); } return r; } @@ -540,6 +546,34 @@ PythonQtObjectPtr PythonQt::parseFile(const QString& filename) return p; } +PythonQtObjectPtr PythonQt::createModuleFromFile(const QString& name, const QString& filename) +{ + PythonQtObjectPtr code = parseFile(filename); + PythonQtObjectPtr module = _p->createModule(name, code); + return module; +} + +PythonQtObjectPtr PythonQt::createModuleFromScript(const QString& name, const QString& script) +{ + PyErr_Clear(); + QString scriptCode = script; + if (scriptCode.isEmpty()) { + // we always need at least a linefeed + scriptCode = "\n"; + } + PythonQtObjectPtr pycode; + pycode.setNewRef(Py_CompileString((char*)scriptCode.toLatin1().data(), "", Py_file_input)); + PythonQtObjectPtr module = _p->createModule(name, pycode); + return module; +} + +PythonQtObjectPtr PythonQt::createUniqueModule() +{ + static QString pyQtStr("PythonQt_module"); + QString moduleName = pyQtStr+QString::number(_uniqueModuleCount++); + return createModuleFromScript(moduleName); +} + void PythonQt::addObject(PyObject* module, const QString& name, QObject* object) { PyModule_AddObject(module, name.toLatin1().data(), _p->wrapQObject(object)); @@ -763,6 +797,8 @@ const QList& PythonQt::constructorHandlers() PythonQtPrivate::PythonQtPrivate() { _importInterface = NULL; + _noLongerWrappedCB = NULL; + _wrappedCB = NULL; } void PythonQtPrivate::addDecorators(QObject* o, bool instanceDeco, bool classDeco) @@ -829,20 +865,9 @@ QList PythonQtPrivate::getDecoratorSlots(const QByteArray& cl return _knownQtDecoratorSlots.values(className); } -void PythonQtPrivate::wrappedObjectDestroyed(QObject* obj) -{ - // mlabDebugConst("MLABPython","PyWrapper QObject destroyed " << o << " " << o->name() << " " << o->className()); - PythonQtWrapper* wrap = _wrappedObjects[obj]; - if (wrap) { - _wrappedObjects.remove(obj); - // remove the pointer but keep the wrapper alive in python - wrap->_obj = NULL; - } -} - -void PythonQtPrivate::destroyedSignalEmitter(QObject* obj) +void PythonQtPrivate::removeSignalEmitter(QObject* obj) { - _signalReceivers.take(obj); + _signalReceivers.remove(obj); } bool PythonQt::handleError() @@ -892,6 +917,16 @@ void PythonQt::stdErrRedirectCB(const QString& str) emit PythonQt::self()->pythonStdErr(str); } +void PythonQt::setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb) +{ + _p->_wrappedCB = cb; +} + +void PythonQt::setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb) +{ + _p->_noLongerWrappedCB = cb; +} + static PyMethodDef PythonQtMethods[] = { @@ -944,3 +979,33 @@ PyObject* PythonQt::helpCalled(PythonQtClassInfo* info) return PyString_FromString(info->help().toLatin1().data()); } } + +void PythonQtPrivate::removeWrapperPointer(void* obj) +{ + _wrappedObjects.remove(obj); +} + +PythonQtWrapper* PythonQtPrivate::findWrapperAndRemoveUnused(void* obj) +{ + PythonQtWrapper* wrap = _wrappedObjects.value(obj); + if (wrap && !wrap->_wrappedPtr && wrap->_obj == NULL) { + // this is a wrapper whose QObject was already removed due to destruction + // so the obj pointer has to be a new QObject with the same address... + // we remove the old one and set the copy to NULL + wrap->_objPointerCopy = NULL; + removeWrapperPointer(obj); + wrap = NULL; + } + return wrap; +} + +PythonQtObjectPtr PythonQtPrivate::createModule(const QString& name, PyObject* pycode) +{ + PythonQtObjectPtr result; + if (pycode) { + result.setNewRef(PyImport_ExecCodeModule((char*)name.toLatin1().data(), pycode)); + } else { + PythonQt::self()->handleError(); + } + return result; +} diff --git a/src/PythonQt.h b/src/PythonQt.h index ded792b..3b7e549 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -66,6 +66,9 @@ class PythonQtImportFileInterface; class PythonQtCppWrapperFactory; class PythonQtConstructorHandler; +typedef void PythonQtQObjectWrappedCB(QObject* object); +typedef void PythonQtQObjectNoLongerWrappedCB(QObject* object); + //! the main interface to the Python Qt binding, realized as a singleton class PYTHONQT_EXPORT PythonQt : public QObject { @@ -137,6 +140,22 @@ public: //! evaluates the given script code from file void evalFile(PyObject* module, const QString& filename); + //! creates the new module \c name and evaluates the given file in the context of that module + //! If the \c script is empty, the module contains no initial code. You can use evalScript/evalCode to add code + //! to a module later on. + //! The user needs to make sure that the \c name is unique in the python module dictionary. + PythonQtObjectPtr createModuleFromFile(const QString& name, const QString& filename); + + //! creates the new module \c name and evaluates the given script in the context of that module. + //! If the \c script is empty, the module contains no initial code. You can use evalScript/evalCode to add code + //! to a module later on. + //! The user needs to make sure that the \c name is unique in the python module dictionary. + PythonQtObjectPtr createModuleFromScript(const QString& name, const QString& script = QString()); + + //! create a uniquely named module, you can use evalFile or evalScript to populate the module with + //! script code + PythonQtObjectPtr createUniqueModule(); + //@{ Signal handlers //! add a signal handler to the given \c signal of \c obj and connect it to a callable \c objectname in module @@ -280,6 +299,14 @@ public: //! The error is currently just output to the python stderr, future version might implement better trace printing bool handleError(); + //! set a callback that is called when a QObject with parent == NULL is wrapped by pythonqt + void setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb); + //! set a callback that is called when a QObject with parent == NULL is no longer wrapped by pythonqt + void setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb); + + //! call the callback if it is set + static void qObjectNoLongerWrappedCB(QObject* o); + signals: //! emitted when python outputs something to stdout (and redirection is turned on) void pythonStdOut(const QString& str); @@ -313,6 +340,7 @@ private: ~PythonQt(); static PythonQt* _self; + static int _uniqueModuleCount; PythonQtPrivate* _p; @@ -331,7 +359,10 @@ public: bool isPythonQtObjectPtrMetaId(int id) { return _PythonQtObjectPtr_metaId == id; } //! remove the wrapper ptr again - void removeWrapperPointer(void* obj) { _wrappedObjects.take(obj); } + void removeWrapperPointer(void* obj); + + //! called when a signal emitting QObject is destroyed to remove the signal handler from the hash map + void removeSignalEmitter(QObject* obj); //! wrap the given QObject into a Python object (or return existing wrapper!) PyObject* wrapQObject(QObject* obj); @@ -379,15 +410,14 @@ public: //! get the destructor slot for the given classname PythonQtSlotInfo* getDestructorSlot(const QByteArray& className) { return _destructorSlots.value(className); } -protected slots: - //! called when a wrapped QObject is destroyed - void wrappedObjectDestroyed(QObject* obj); - - //! called when a signal emitting QObject is destroyed to remove the signal handler from the hash map - void destroyedSignalEmitter(QObject* obj); + //! creates the new module from the given pycode + PythonQtObjectPtr createModule(const QString& name, PyObject* pycode); private: + //! get the wrapper for a given pointer (and remove a wrapper of an already destroyed qobject) + PythonQtWrapper* findWrapperAndRemoveUnused(void* obj); + //! stores pointer to PyObject mapping of wrapped QObjects AND C++ objects QHash _wrappedObjects; @@ -412,6 +442,9 @@ private: //! the importer interface (if set) PythonQtImportFileInterface* _importInterface; + PythonQtQObjectNoLongerWrappedCB* _noLongerWrappedCB; + PythonQtQObjectWrappedCB* _wrappedCB; + QStringList _importIgnorePaths; //! the cpp object wrapper factories diff --git a/src/PythonQtConversion.cpp b/src/PythonQtConversion.cpp index 507c1e2..0ef2271 100644 --- a/src/PythonQtConversion.cpp +++ b/src/PythonQtConversion.cpp @@ -296,7 +296,8 @@ return Py_None; } } else { if (wrap->_info->inherits(info.name)) { - PythonQtValueStorage_ADD_VALUE(global_ptrStorage, void*, wrap->_obj, ptr); + QObject* myObject = wrap->_obj; + PythonQtValueStorage_ADD_VALUE(global_ptrStorage, void*, myObject, ptr); } else { // not matching } @@ -487,6 +488,8 @@ return Py_None; if (ok) { PythonQtValueStorage_ADD_VALUE(global_valueStorage, unsigned int, val, ptr); return ptr; + } else { + return NULL; } } } @@ -730,7 +733,8 @@ QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type) // is this worth anything? we loose the knowledge of the cpp object type v = qVariantFromValue(wrap->_wrappedPtr); } else { - v = qVariantFromValue(wrap->_obj); + QObject* myObject = wrap->_obj; + v = qVariantFromValue(myObject); } return v; } else if (val->ob_type==&PyDict_Type) { @@ -998,7 +1002,8 @@ bool PythonQtConv::ConvertPythonListToQListOfType(PyObject* obj, QList* l } } else { if (wrap->_info->inherits(type)) { - list->append((void*)wrap->_obj); + QObject* myObject = wrap->_obj; + list->append((void*)myObject); } else { result = false; break; diff --git a/src/PythonQtImporter.cpp b/src/PythonQtImporter.cpp index c1e225a..f27b1cf 100644 --- a/src/PythonQtImporter.cpp +++ b/src/PythonQtImporter.cpp @@ -620,10 +620,12 @@ PythonQtImport::getCodeFromData(const QString& path, int isbytecode,int ispackag else { // mlabDebugConst("MLABPython", "compiling source " << path); code = compileSource(path, qdata); - // save a pyc file if possible - QDateTime time; - time = hasImporter?PythonQt::importInterface()->lastModifiedDate(path):QFileInfo(path).lastModified(); - writeCompiledModule((PyCodeObject*)code, path+"c", time.toTime_t()); + if (code) { + // save a pyc file if possible + QDateTime time; + time = hasImporter?PythonQt::importInterface()->lastModifiedDate(path):QFileInfo(path).lastModified(); + writeCompiledModule((PyCodeObject*)code, path+"c", time.toTime_t()); + } } return code; } @@ -776,7 +778,7 @@ void PythonQtImport::init() PyObject* classobj = PyDict_GetItemString(PyModule_GetDict(mod), "PythonQtImporter"); PyObject* path_hooks = PySys_GetObject("path_hooks"); PyList_Append(path_hooks, classobj); - + #ifndef WIN32 // reload the encodings module, because it might fail to custom import requirements (e.g. encryption). PyObject* modules = PyImport_GetModuleDict(); diff --git a/src/PythonQtMetaObjectWrapper.cpp b/src/PythonQtMetaObjectWrapper.cpp index 590f002..8123344 100644 --- a/src/PythonQtMetaObjectWrapper.cpp +++ b/src/PythonQtMetaObjectWrapper.cpp @@ -143,7 +143,7 @@ static PyObject *PythonQtMetaObjectWrapper_getattro(PyObject *obj,PyObject *name if (member._type == PythonQtMemberInfo::Slot && member._slot->isClassDecorator()) { return PythonQtSlotFunction_New(member._slot, obj, NULL); } - + // look for the interal methods (className(), help()) PyObject* internalMethod = Py_FindMethod( PythonQtMetaObjectWrapper_methods, obj, (char*)attributeName); if (internalMethod) { @@ -194,6 +194,11 @@ static int PythonQtMetaObjectWrapper_compare(PyObject * obj1, PyObject * obj2) } } +static long PythonQtMetaObjectWrapper_hash(PythonQtMetaObjectWrapper *obj) +{ + return reinterpret_cast(obj->_info); +} + PyTypeObject PythonQtMetaObjectWrapper_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ @@ -209,7 +214,7 @@ PyTypeObject PythonQtMetaObjectWrapper_Type = { 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ - 0, /*tp_hash */ + (hashfunc)PythonQtMetaObjectWrapper_hash, /*tp_hash */ PythonQtMetaObjectWrapper_call, /*tp_call*/ 0, /*tp_str*/ PythonQtMetaObjectWrapper_getattro, /*tp_getattro*/ diff --git a/src/PythonQtSignalReceiver.cpp b/src/PythonQtSignalReceiver.cpp index e83fd2e..ec8ac26 100644 --- a/src/PythonQtSignalReceiver.cpp +++ b/src/PythonQtSignalReceiver.cpp @@ -45,12 +45,46 @@ #include "PythonQtConversion.h" #include #include +#include "funcobject.h" void PythonQtSignalTarget::call(void **arguments) const { + // Note: we check if the callable is a PyFunctionObject and has a fixed number of arguments + // if that is the case, we only pass these arguments to python and skip the additional arguments from the signal + + int numPythonArgs = -1; + if (PyFunction_Check(_callable)) { + PyObject* o = _callable; + PyFunctionObject* func = (PyFunctionObject*)o; + PyCodeObject* code = (PyCodeObject*)func->func_code; + if (!(code->co_flags & 0x04)) { + numPythonArgs = code->co_argcount; + } else { + // variable numbers of arguments allowed + } + } else if (PyMethod_Check(_callable)) { + PyObject* o = _callable; + PyMethodObject* method = (PyMethodObject*)o; + if (PyFunction_Check(method->im_func)) { + PyFunctionObject* func = (PyFunctionObject*)method->im_func; + PyCodeObject* code = (PyCodeObject*)func->func_code; + if (!(code->co_flags & 0x04)) { + numPythonArgs = code->co_argcount - 1; // we subtract one because the first is "self" + } else { + // variable numbers of arguments allowed + } + } + } + const PythonQtMethodInfo* m = methodInfo(); - // paramterCount includes return value: + // parameterCount includes return value: int count = m->parameterCount(); + if (numPythonArgs!=-1) { + if (count>numPythonArgs+1) { + // take less arguments + count = numPythonArgs+1; + } + } PyObject* pargs = NULL; if (count>1) { @@ -97,6 +131,7 @@ PythonQtSignalReceiver::PythonQtSignalReceiver(QObject* obj):PythonQtSignalRecei PythonQtSignalReceiver::~PythonQtSignalReceiver() { + PythonQt::priv()->removeSignalEmitter(_obj); } diff --git a/src/PythonQtStdOut.cpp b/src/PythonQtStdOut.cpp index 091f3b6..f103a65 100644 --- a/src/PythonQtStdOut.cpp +++ b/src/PythonQtStdOut.cpp @@ -70,10 +70,18 @@ static PyObject *PythonQtStdOutRedirect_write(PyObject *self, PyObject *args) return Py_BuildValue(""); } +static PyObject *PythonQtStdOutRedirect_flush(PyObject *self, PyObject *args) +{ + return Py_BuildValue(""); +} + + static PyMethodDef PythonQtStdOutRedirect_methods[] = { {"write", (PyCFunction)PythonQtStdOutRedirect_write, METH_VARARGS, - "redirect the writing to a callback" + "redirect the writing to a callback"}, + {"flush", (PyCFunction)PythonQtStdOutRedirect_flush, METH_VARARGS, + "flush the output, currently not implemented but needed for logging framework" }, {NULL} /* Sentinel */ }; diff --git a/src/PythonQtVariantWrapper.cpp b/src/PythonQtVariantWrapper.cpp index 1ee838b..1855d39 100644 --- a/src/PythonQtVariantWrapper.cpp +++ b/src/PythonQtVariantWrapper.cpp @@ -171,6 +171,18 @@ QString qVariantToString(const QVariant& v) { case QVariant::Time: r = v.toTime().toString(Qt::ISODate); break; + case QVariant::Pixmap: + { + QPixmap p = qvariant_cast(v); + r = QString("Pixmap ") + QString::number(p.width()) + ", " + QString::number(p.height()); + } + break; + case QVariant::Image: + { + QImage img = qvariant_cast(v); + r = QString("Image ") + QString::number(img.width()) + ", " + QString::number(img.height()); + } + break; //TODO: add more printing for other variant types default: r = v.toString(); @@ -182,6 +194,9 @@ static PyObject * PythonQtVariantWrapper_str(PyObject * obj) { PythonQtVariantWrapper* wt = (PythonQtVariantWrapper*)obj; QString val = qVariantToString(*wt->_variant); + if (val.isEmpty()) { + val = wt->_variant->typeName(); + } return PyString_FromFormat("%s", val.toLatin1().constData()); } diff --git a/src/PythonQtWrapper.cpp b/src/PythonQtWrapper.cpp index b6780ef..663b98a 100644 --- a/src/PythonQtWrapper.cpp +++ b/src/PythonQtWrapper.cpp @@ -49,9 +49,9 @@ static void PythonQtWrapper_dealloc(PythonQtWrapper* self) { if (self->_wrappedPtr) { - + //mlabDebugConst("Python","c++ wrapper removed " << self->_wrappedPtr << " " << self->_obj->className() << " " << self->_info->wrappedClassName().latin1()); - + PythonQt::priv()->removeWrapperPointer(self->_wrappedPtr); // we own our qobject, so we delete it now: delete self->_obj; @@ -68,27 +68,37 @@ static void PythonQtWrapper_dealloc(PythonQtWrapper* self) // TODO: print a warning? we can not destroy that object } } - } else if (self->_obj) { + } else { //mlabDebugConst("Python","qobject wrapper removed " << self->_obj->className() << " " << self->_info->wrappedClassName().latin1()); - PythonQt::priv()->removeWrapperPointer(self->_obj); - if (self->_ownedByPythonQt) { - if (!self->_obj->parent()) { - delete self->_obj; - self->_obj = NULL; + if (self->_objPointerCopy) { + PythonQt::priv()->removeWrapperPointer(self->_objPointerCopy); + } + if (self->_obj) { + if (self->_ownedByPythonQt) { + if (!self->_obj->parent()) { + delete self->_obj; + } + } else { + if (self->_obj->parent()==NULL) { + // tell someone who is interested that the qobject is no longer wrapped, if it has no parent + PythonQt::qObjectNoLongerWrappedCB(self->_obj); + } } } } + self->_obj = NULL; + self->_obj.~QPointer(); self->ob_type->tp_free((PyObject*)self); } static PyObject* PythonQtWrapper_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PythonQtWrapper *self; - + self = (PythonQtWrapper *)type->tp_alloc(type, 0); if (self != NULL) { self->_info = NULL; - self->_obj = NULL; + new (&self->_obj) QPointer(); self->_wrappedPtr = NULL; self->_ownedByPythonQt = false; } @@ -126,21 +136,21 @@ static PyObject *PythonQtWrapper_getattro(PyObject *obj,PyObject *name) { const char *attributeName; PythonQtWrapper *wt = (PythonQtWrapper *)obj; - + if ((attributeName = PyString_AsString(name)) == NULL) { return NULL; } - + if (!wt->_obj && !wt->_wrappedPtr) { QString error = QString("Trying to read attribute '") + attributeName + "' from a destroyed " + wt->_info->className() + " object"; PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); return NULL; } - + // mlabDebugConst("Python","get " << attributeName); - + // TODO: dynamic properties are missing - + PythonQtMemberInfo member = wt->_info->member(attributeName); switch (member._type) { case PythonQtMemberInfo::Property: @@ -155,7 +165,7 @@ static PyObject *PythonQtWrapper_getattro(PyObject *obj,PyObject *name) return PyInt_FromLong(member._enumValue); break; } - + // look for the interal methods (className(), help()) PyObject* internalMethod = Py_FindMethod( PythonQtWrapper_methods, obj, (char*)attributeName); if (internalMethod) { @@ -186,7 +196,7 @@ static PyObject *PythonQtWrapper_getattro(PyObject *obj,PyObject *name) return dict; } - + QString error = QString(wt->_info->className()) + " has no attribute named '" + QString(attributeName) + "'"; PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); return NULL; @@ -197,16 +207,16 @@ static int PythonQtWrapper_setattro(PyObject *obj,PyObject *name,PyObject *value QString error; char *attributeName; PythonQtWrapper *wt = (PythonQtWrapper *)obj; - + if ((attributeName = PyString_AsString(name)) == NULL) return -1; - + if (!wt->_obj) { error = QString("Trying to set attribute '") + attributeName + "' on a destroyed " + wt->_info->className() + " object"; PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); return -1; } - + PythonQtMemberInfo member = wt->_info->member(attributeName); if (member._type == PythonQtMemberInfo::Property) { QMetaProperty prop = member._property; @@ -240,7 +250,7 @@ static int PythonQtWrapper_setattro(PyObject *obj,PyObject *name,PyObject *value error = QString("EnumValue '") + attributeName + "' can not be overwritten on " + wt->_info->className() + " object"; } } - + PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); return -1; } @@ -248,14 +258,15 @@ static int PythonQtWrapper_setattro(PyObject *obj,PyObject *name,PyObject *value static PyObject * PythonQtWrapper_repr(PyObject * obj) { PythonQtWrapper* wt = (PythonQtWrapper*)obj; + QObject *qobj = wt->_obj; if (wt->_wrappedPtr) { if (wt->_obj) { - return PyString_FromFormat("%s (C++ Object 0x%x wrapped by %s 0x%x))", wt->_info->className(), wt->_wrappedPtr, wt->_obj->metaObject()->className(), wt->_obj); + return PyString_FromFormat("%s (C++ Object %p wrapped by %s %p))", wt->_info->className(), wt->_wrappedPtr, wt->_obj->metaObject()->className(), qobj); } else { - return PyString_FromFormat("%s (C++ Object 0x%x unwrapped)", wt->_info->className(), wt->_wrappedPtr); + return PyString_FromFormat("%s (C++ Object %p unwrapped)", wt->_info->className(), wt->_wrappedPtr); } } else { - return PyString_FromFormat("%s (QObject 0x%x)", wt->_info->className(), wt->_obj, wt->_wrappedPtr); + return PyString_FromFormat("%s (QObject %p)", wt->_info->className(), qobj); } } @@ -263,7 +274,7 @@ static int PythonQtWrapper_compare(PyObject * obj1, PyObject * obj2) { if (obj1->ob_type == &PythonQtWrapper_Type && obj2->ob_type == &PythonQtWrapper_Type) { - + PythonQtWrapper* w1 = (PythonQtWrapper*)obj1; PythonQtWrapper* w2 = (PythonQtWrapper*)obj2; if (w1->_wrappedPtr != NULL) { @@ -288,6 +299,19 @@ static int PythonQtWrapper_nonzero(PyObject *obj) return (wt->_wrappedPtr == NULL && wt->_obj == NULL)?0:1; } + +static long PythonQtWrapper_hash(PythonQtWrapper *obj) +{ + if (obj->_wrappedPtr != NULL) { + return reinterpret_cast(obj->_wrappedPtr); + } else { + QObject* qobj = obj->_obj; // get pointer from QPointer wrapper + return reinterpret_cast(qobj); + } +} + + + // we override nb_nonzero, so that one can do 'if' expressions to test for a NULL ptr static PyNumberMethods PythonQtWrapper_as_number = { 0, /* nb_add */ @@ -345,7 +369,7 @@ PyTypeObject PythonQtWrapper_Type = { &PythonQtWrapper_as_number, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ - 0, /*tp_hash */ + (hashfunc)PythonQtWrapper_hash, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ PythonQtWrapper_getattro, /*tp_getattro*/ diff --git a/src/PythonQtWrapper.h b/src/PythonQtWrapper.h index 493c4cd..6690107 100644 --- a/src/PythonQtWrapper.h +++ b/src/PythonQtWrapper.h @@ -45,6 +45,7 @@ #include #include "PythonQtSystem.h" +#include #include "structmember.h" #include "methodobject.h" @@ -61,8 +62,18 @@ extern PYTHONQT_EXPORT PyTypeObject PythonQtWrapper_Type; typedef struct { PyObject_HEAD + //! set the QObject pointer + void setQObject(QObject* object) { + _obj = object; + _objPointerCopy = object; + } + //! pointer to the wrapped Qt object or if _wrappedPtr is set, the Qt object that wraps the C++ Ptr - QObject* _obj; + QPointer _obj; + //! a copy of the _obj pointer, which is required because the wrapper needs to + //! deregister itself via the _obj pointer, even when the QPointer object was destroyed + void* _objPointerCopy; + //! optional C++ object Ptr that is wrapped by the above _obj void* _wrappedPtr;