From 7f1e2590af3149b706ca7c98150847d82fb66cfd 2012-04-23 09:53:14 From: florianlink Date: 2012-04-23 09:53:14 Subject: [PATCH] Updated from current MeVisLab development git-svn-id: svn://svn.code.sf.net/p/pythonqt/code/trunk@212 ea8d5007-eb21-0410-b261-ccb3ea6e24a9 --- diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index e7e0509..7e9a69a 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -43,6 +43,7 @@ #include "PythonQtImporter.h" #include "PythonQtClassInfo.h" #include "PythonQtMethodInfo.h" +#include "PythonQtSignal.h" #include "PythonQtSignalReceiver.h" #include "PythonQtConversion.h" #include "PythonQtStdIn.h" @@ -167,6 +168,11 @@ PythonQt::PythonQt(int flags, const QByteArray& pythonQtModuleName) } Py_INCREF(&PythonQtSlotFunction_Type); + if (PyType_Ready(&PythonQtSignalFunction_Type) < 0) { + std::cerr << "could not initialize PythonQtSignalFunction_Type" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; + } + Py_INCREF(&PythonQtSignalFunction_Type); + // 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 @@ -412,6 +418,20 @@ PyObject* PythonQtPrivate::wrapPtr(void* ptr, const QByteArray& name) if (info) { // try to downcast in the class hierarchy, which will modify info and ptr if it is successfull ptr = info->castDownIfPossible(ptr, &info); + + // if downcasting found out that the object is a QObject, + // handle it like one: + if (info && info->isQObject()) { + QObject* qptr = (QObject*)ptr; + // if the object is a derived object, we want to switch the class info to the one of the derived class: + if (name!=(qptr->metaObject()->className())) { + registerClass(qptr->metaObject()); + info = _knownClassInfos.value(qptr->metaObject()->className()); + } + wrap = createNewPythonQtInstanceWrapper(qptr, info); + // mlabDebugConst("MLABPython","new qobject wrapper added " << " " << wrap->_obj->className() << " " << wrap->classInfo()->wrappedClassName().latin1()); + return (PyObject*)wrap; + } } if (!info || info->pythonQtClassWrapper()==NULL) { @@ -602,7 +622,6 @@ PythonQtObjectPtr PythonQt::lookupObject(PyObject* module, const QString& name) QStringList l = name.split('.'); PythonQtObjectPtr p = module; PythonQtObjectPtr prev; - QString s; QByteArray b; for (QStringList::ConstIterator i = l.begin(); i!=l.end() && p; ++i) { prev = p; @@ -784,97 +803,171 @@ QStringList PythonQt::introspection(PyObject* module, const QString& objectname, } if (object) { - if (type == CallOverloads) { - if (PythonQtSlotFunction_Check(object)) { - PythonQtSlotFunctionObject* o = (PythonQtSlotFunctionObject*)object.object(); - PythonQtSlotInfo* info = o->m_ml; - - while (info) { - results << info->fullSignature(); - info = info->nextInfo(); - } - } else if (object->ob_type == &PythonQtClassWrapper_Type) { - PythonQtClassWrapper* o = (PythonQtClassWrapper*)object.object(); - PythonQtSlotInfo* info = o->classInfo()->constructors(); + results = introspectObject(object, type); + } + + return results; +} - while (info) { - results << info->fullSignature(); - info = info->nextInfo(); - } +QStringList PythonQt::introspectObject(PyObject* object, ObjectType type) +{ + QStringList results; + + if (type == CallOverloads) { + if (PythonQtSlotFunction_Check(object)) { + PythonQtSlotFunctionObject* o = (PythonQtSlotFunctionObject*)object; + PythonQtSlotInfo* info = o->m_ml; + + while (info) { + results << info->fullSignature(); + info = info->nextInfo(); + } + } else if (PythonQtSignalFunction_Check(object)) { + PythonQtSignalFunctionObject* o = (PythonQtSignalFunctionObject*)object; + PythonQtSlotInfo* info = o->m_ml; + + while (info) { + results << info->fullSignature(); + info = info->nextInfo(); + } + } else if (object->ob_type == &PythonQtClassWrapper_Type) { + PythonQtClassWrapper* o = (PythonQtClassWrapper*)object; + PythonQtSlotInfo* info = o->classInfo()->constructors(); + + while (info) { + results << info->fullSignature(); + info = info->nextInfo(); + } + } else { + QString signature = _p->getSignature(object); + if (!signature.isEmpty()) { + results << signature; } else { - //TODO: use pydoc! PyObject* doc = PyObject_GetAttrString(object, "__doc__"); if (doc) { results << PyString_AsString(doc); Py_DECREF(doc); } } + } + } else { + PyObject* keys = NULL; + bool isDict = false; + if (PyDict_Check(object)) { + keys = PyDict_Keys(object); + isDict = true; } else { - PyObject* keys = NULL; - bool isDict = false; - if (PyDict_Check(object)) { - keys = PyDict_Keys(object); - isDict = true; - } else { - keys = PyObject_Dir(object); - } - if (keys) { - int count = PyList_Size(keys); - PyObject* key; - PyObject* value; - QString keystr; - for (int i = 0;iob_type == &PyClass_Type || value->ob_type == &PyType_Type) { + results << keystr; + } + break; + case Variable: + if (value->ob_type != &PyClass_Type + && value->ob_type != &PyCFunction_Type + && value->ob_type != &PyFunction_Type + && value->ob_type != &PyMethod_Type + && value->ob_type != &PyModule_Type + && value->ob_type != &PyType_Type + && value->ob_type != &PythonQtSlotFunction_Type + ) { + results << keystr; + } + break; + case Function: + if (value->ob_type == &PyCFunction_Type || + value->ob_type == &PyFunction_Type || + value->ob_type == &PyMethod_Type || + value->ob_type == &PythonQtSlotFunction_Type + ) { results << keystr; - break; - case Class: - if (value->ob_type == &PyClass_Type) { - results << keystr; - } - break; - case Variable: - if (value->ob_type != &PyClass_Type - && value->ob_type != &PyCFunction_Type - && value->ob_type != &PyFunction_Type - && value->ob_type != &PyModule_Type - ) { - results << keystr; - } - break; - case Function: - if (value->ob_type == &PyFunction_Type || - value->ob_type == &PyMethod_Type - ) { - results << keystr; - } - break; - case Module: - if (value->ob_type == &PyModule_Type) { - results << keystr; - } - break; - default: - std::cerr << "PythonQt: introspection: unknown case" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; } + break; + case Module: + if (value->ob_type == &PyModule_Type) { + results << keystr; + } + break; + default: + std::cerr << "PythonQt: introspection: unknown case" << ", in " << __FILE__ << ":" << __LINE__ << std::endl; } - Py_DECREF(value); } - Py_DECREF(keys); + Py_DECREF(value); } + Py_DECREF(keys); + } + } + return results; +} + +PyObject* PythonQt::getObjectByType(const QString& typeName) +{ + PythonQtObjectPtr sys; + sys.setNewRef(PyImport_ImportModule("sys")); + PythonQtObjectPtr modules = lookupObject(sys, "modules"); + Q_ASSERT(PyDict_Check(modules)); + + QStringList tmp = typeName.split("."); + QString simpleTypeName = tmp.takeLast(); + QString moduleName = tmp.join("."); + + PyObject* object = NULL; + PyObject* moduleObject = PyDict_GetItemString(modules, moduleName.toLatin1().constData()); + if (moduleObject) { + object = PyObject_GetAttrString(moduleObject, simpleTypeName.toLatin1().constData()); + } + + if (!object) { + moduleObject = PyDict_GetItemString(modules, "__builtin__"); + if (moduleObject) { + object = PyObject_GetAttrString(moduleObject, simpleTypeName.toLatin1().constData()); + } + } + + return object; +} + +QStringList PythonQt::introspectType(const QString& typeName, ObjectType type) +{ + QStringList results; + PyObject* object = getObjectByType(typeName); + if (!object) { + // the last item may be a member, split it away and try again + QStringList tmp = typeName.split("."); + QString memberName = tmp.takeLast(); + QString typeName = tmp.takeLast(); + PyObject* typeObject = getObjectByType(typeName); + if (typeObject) { + object = PyObject_GetAttrString(typeObject, memberName.toLatin1().constData()); } } + if (object) { + results = introspectObject(object, type); + Py_DECREF(object); + } return results; } @@ -1190,6 +1283,107 @@ void PythonQt::initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQ } } +QString PythonQt::getReturnTypeOfWrappedMethod(PyObject* module, const QString& name) +{ + QStringList tmp = name.split("."); + QString methodName = tmp.takeLast(); + QString variableName = tmp.join("."); + // TODO: the variableName may be a type name, this needs to be handled differently, + // because it is not necessarily known in the module context + PythonQtObjectPtr variableObject = lookupObject(module, variableName); + if (variableObject.isNull()) { + return ""; + } + + return getReturnTypeOfWrappedMethodHelper(variableObject, methodName, name); +} + +QString PythonQt::getReturnTypeOfWrappedMethod(const QString& typeName, const QString& methodName) +{ + PythonQtObjectPtr typeObject = getObjectByType(typeName); + if (typeObject.isNull()) { + return ""; + } + return getReturnTypeOfWrappedMethodHelper(typeObject, methodName, typeName + "." + methodName); +} + +QString PythonQt::getReturnTypeOfWrappedMethodHelper(const PythonQtObjectPtr& variableObject, const QString& methodName, const QString& context) +{ + PythonQtObjectPtr methodObject; + if (PyDict_Check(variableObject)) { + methodObject = PyDict_GetItemString(variableObject, methodName.toLatin1().constData()); + } else { + methodObject.setNewRef(PyObject_GetAttrString(variableObject, methodName.toLatin1().constData())); + } + if (methodObject.isNull()) { + return ""; + } + + QString type; + + if (methodObject->ob_type == &PyClass_Type || methodObject->ob_type == &PyType_Type) { + // the methodObject is not a method, but the name of a type/class. This means + // a constructor is called. Return the context. + type = context; + } else if (methodObject->ob_type == &PythonQtSlotFunction_Type) { + QString className; + + if (PyObject_TypeCheck(variableObject, &PythonQtInstanceWrapper_Type)) { + // the type name of wrapped instance is the class name + className = variableObject->ob_type->tp_name; + } else { + PyObject* classNameObject = PyObject_GetAttrString(variableObject, "__name__"); + if (classNameObject) { + Q_ASSERT(PyString_Check(classNameObject)); + className = PyString_AsString(classNameObject); + Py_DECREF(classNameObject); + } + } + + if (!className.isEmpty()) { + PythonQtClassInfo* info = _p->_knownClassInfos.value(className.toLatin1().constData()); + if (info) { + PythonQtSlotInfo* slotInfo = info->member(methodName.toLatin1().constData())._slot; + if (slotInfo) { + if (slotInfo->metaMethod()) { + type = slotInfo->metaMethod()->typeName(); + if (!type.isEmpty()) { + QChar c = type.at(type.length()-1); + while (c == '*' || c == '&') { + type.truncate(type.length()-1); + if (!type.isEmpty()) { + c = type.at(type.length()-1); + } else { + break; + } + } + // split away template arguments + type = type.split("<").first(); + // split away const + type = type.split(" ").last().trimmed(); + + // if the type is a known class info, then create the full type name, i.e. include the + // module name. For example, the slot may return a QDate, then this looks up the + // name _PythonQt.QtCore.QDate. + PythonQtClassInfo* typeInfo = _p->_knownClassInfos.value(type.toLatin1().constData()); + if (typeInfo && typeInfo->pythonQtClassWrapper()) { + PyObject* s = PyObject_GetAttrString(typeInfo->pythonQtClassWrapper(), "__module__"); + Q_ASSERT(PyString_Check(s)); + type = QString(PyString_AsString(s)) + "." + type; + Py_DECREF(s); + s = PyObject_GetAttrString(typeInfo->pythonQtClassWrapper(), "__name__"); + Q_ASSERT(PyString_Check(s)); + Py_DECREF(s); + } + } + } + } + } + } + } + return type; +} + void PythonQt::registerCPPClass(const char* typeName, const char* parentTypeName, const char* package, PythonQtQObjectCreatorFunctionCB* wrapperCreator, PythonQtShellSetInstanceWrapperCB* shell) { _p->registerCPPClass(typeName, parentTypeName, package, wrapperCreator, shell); @@ -1349,4 +1543,150 @@ void* PythonQtPrivate::unwrapForeignWrapper( const QByteArray& classname, PyObje } } return NULL; -} \ No newline at end of file +} + +bool PythonQtPrivate::isMethodDescriptor(PyObject* object) const +{ + // This implementation is the same as in inspect.ismethoddescriptor(), inspect.py. + if (PyObject_HasAttrString(object, "__get__") && + !PyObject_HasAttrString(object, "__set__") && + !PyMethod_Check(object) && + !PyFunction_Check(object) && + !PyClass_Check(object)) { + return true; + } + return false; +} + +QString PythonQtPrivate::getSignature(PyObject* object) +{ + QString signature; + + if (object) { + PyMethodObject* method = NULL; + PyFunctionObject* func = NULL; + + bool decrefMethod = false; + + if (object->ob_type == &PyClass_Type || object->ob_type == &PyType_Type) { + method = (PyMethodObject*)PyObject_GetAttrString(object, "__init__"); + decrefMethod = true; + } else if (object->ob_type == &PyFunction_Type) { + func = (PyFunctionObject*)object; + } else if (object->ob_type == &PyMethod_Type) { + method = (PyMethodObject*)object; + } + if (method) { + if (PyFunction_Check(method->im_func)) { + func = (PyFunctionObject*)method->im_func; + } else if (isMethodDescriptor((PyObject*)method)) { + QString docstr; + PyObject* doc = PyObject_GetAttrString(object, "__doc__"); + if (doc) { + docstr = PyString_AsString(doc); + Py_DECREF(doc); + } + + PyObject* s = PyObject_GetAttrString(object, "__name__"); + if (s) { + Q_ASSERT(PyString_Check(s)); + signature = PyString_AsString(s); + if (docstr.startsWith(signature + "(")) { + signature = docstr; + } else { + signature += "(...)"; + if (!docstr.isEmpty()) { + signature += "\n\n" + docstr; + } + } + Py_DECREF(s); + } + } + } + + if (func) { + QString funcName; + PyObject* s = PyObject_GetAttrString((PyObject*)func, "__name__"); + if (s) { + Q_ASSERT(PyString_Check(s)); + funcName = PyString_AsString(s); + Py_DECREF(s); + } + if (method && funcName == "__init__") { + PyObject* s = PyObject_GetAttrString(object, "__name__"); + if (s) { + Q_ASSERT(PyString_Check(s)); + funcName = PyString_AsString(s); + Py_DECREF(s); + } + } + + QStringList arguments; + QStringList defaults; + QString varargs; + QString varkeywords; + // NOTE: This implementation is based on function getargs() in inspect.py. + // inspect.getargs() can handle anonymous (tuple) arguments, while this code does not. + // It can be implemented, but it may be rarely needed and not necessary. + PyCodeObject* code = (PyCodeObject*)func->func_code; + if (code->co_varnames) { + int nargs = code->co_argcount; + Q_ASSERT(PyTuple_Check(code->co_varnames)); + for (int i=0; ico_varnames, i); + Q_ASSERT(PyString_Check(name)); + arguments << PyString_AsString(name); + } + if (code->co_flags & CO_VARARGS) { + PyObject* s = PyTuple_GetItem(code->co_varnames, nargs); + Q_ASSERT(PyString_Check(s)); + varargs = PyString_AsString(s); + nargs += 1; + } + if (code->co_flags & CO_VARKEYWORDS) { + PyObject* s = PyTuple_GetItem(code->co_varnames, nargs); + Q_ASSERT(PyString_Check(s)); + varkeywords = PyString_AsString(s); + } + } + + PyObject* defaultsTuple = func->func_defaults; + if (defaultsTuple) { + Q_ASSERT(PyTuple_Check(defaultsTuple)); + for (Py_ssize_t i=0; i0 || arguments[i] != "self") { + signature += arguments[i]; + if (i >= firstdefault) { + signature += "=" + defaults[i-firstdefault]; + } + } + } + if (!varargs.isEmpty()) { + if (!signature.isEmpty()) { signature += ", "; } + signature += "*" + varargs; + } + if (!varkeywords.isEmpty()) { + if (!signature.isEmpty()) { signature += ", "; } + signature += "**" + varkeywords; + } + signature = funcName + "(" + signature + ")"; + } + + if (method && decrefMethod) { + Py_DECREF(method); + } + } + + return signature; +} diff --git a/src/PythonQt.h b/src/PythonQt.h index 63cbbb7..a064342 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -333,11 +333,22 @@ public: //! read vars etc. in scope of an \c object, optional looking inside of an object \c objectname QStringList introspection(PyObject* object, const QString& objectname, ObjectType type); + //! read vars etc. in scope of the given \c object + QStringList introspectObject(PyObject* object, ObjectType type); + //! read vars etc. in scope of the type object called \c typename. First the typename + //! of the form module.type is split into module and type. Then the module is looked up + //! in sys.modules. If the module or type is not found there, then the type is looked up in + //! the __builtin__ module. + QStringList introspectType(const QString& typeName, ObjectType type); //! returns the found callable object or NULL //! @return new reference PythonQtObjectPtr lookupCallable(PyObject* object, const QString& name); - + + //! returns the return type of the method of a wrapped c++ object referenced by \c objectname + QString getReturnTypeOfWrappedMethod(PyObject* module, const QString& objectname); + //! returns the return type of the method \c methodName of a wrapped c++ type referenced by \c typeName + QString getReturnTypeOfWrappedMethod(const QString& typeName, const QString& methodName); //@} //--------------------------------------------------------------------------- @@ -501,6 +512,10 @@ signals: private: void initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQtModuleName); + + QString getReturnTypeOfWrappedMethodHelper(const PythonQtObjectPtr& variableObject, const QString& methodName, const QString& context); + + PyObject* getObjectByType(const QString& typeName); //! callback for stdout redirection, emits pythonStdOut signal static void stdOutRedirectCB(const QString& str); @@ -626,6 +641,12 @@ public: //! returns the profiling callback, which may be NULL PythonQt::ProfilingCB* profilingCB() const { return _profilingCB; } + + //! determines the signature of the given callable object (similar as pydoc) + QString getSignature(PyObject* object); + + //! returns true if the object is a method descriptor (same as inspect.ismethoddescriptor() in inspect.py) + bool isMethodDescriptor(PyObject* object) const; private: //! Setup the shared library suffixes by getting them from the "imp" module. diff --git a/src/PythonQtClassInfo.cpp b/src/PythonQtClassInfo.cpp index 691b440..837aeca 100644 --- a/src/PythonQtClassInfo.cpp +++ b/src/PythonQtClassInfo.cpp @@ -96,7 +96,7 @@ void PythonQtClassInfo::clearCachedMembers() QHashIterator i(_cachedMembers); while (i.hasNext()) { PythonQtMemberInfo member = i.next().value(); - if (member._type== PythonQtMemberInfo::Slot) { + if (member._type== PythonQtMemberInfo::Slot || member._type== PythonQtMemberInfo::Signal) { PythonQtSlotInfo* info = member._slot; while (info) { PythonQtSlotInfo* next = info->nextInfo(); @@ -462,18 +462,14 @@ QStringList PythonQtClassInfo::propertyList() return l; } -QStringList PythonQtClassInfo::memberList(bool metaOnly) +QStringList PythonQtClassInfo::memberList() { decorator(); QStringList l; QString h; - if (_isQObject && _meta && !metaOnly) { - l = propertyList(); - } - // normal slots of QObject (or wrapper QObject) - if (!metaOnly && _meta) { + if (_meta) { int numMethods = _meta->methodCount(); bool skipQObj = !_isQObject; for (int i = skipQObj?QObject::staticMetaObject.methodCount():0; i < numMethods; i++) { @@ -493,7 +489,7 @@ QStringList PythonQtClassInfo::memberList(bool metaOnly) QList infos; recursiveCollectClassInfos(infos); foreach(PythonQtClassInfo* info, infos) { - info->listDecoratorSlotsFromDecoratorProvider(l, metaOnly); + info->listDecoratorSlotsFromDecoratorProvider(l, false); } } @@ -866,3 +862,33 @@ void PythonQtClassInfo::clearNotFoundCachedMembers() } } } + +//------------------------------------------------------------------------- + +PythonQtMemberInfo::PythonQtMemberInfo( PythonQtSlotInfo* info ) +{ + if (info->metaMethod()->methodType() == QMetaMethod::Signal) { + _type = Signal; + } else { + _type = Slot; + } + _slot = info; + _enumValue = NULL; +} + +PythonQtMemberInfo::PythonQtMemberInfo( const PythonQtObjectPtr& enumValue ) +{ + _type = EnumValue; + _slot = NULL; + _enumValue = enumValue; + _enumWrapper = NULL; +} + +PythonQtMemberInfo::PythonQtMemberInfo( const QMetaProperty& prop ) +{ + _type = Property; + _slot = NULL; + _enumValue = NULL; + _property = prop; + _enumWrapper = NULL; +} \ No newline at end of file diff --git a/src/PythonQtClassInfo.h b/src/PythonQtClassInfo.h index e8d532e..517d8c7 100644 --- a/src/PythonQtClassInfo.h +++ b/src/PythonQtClassInfo.h @@ -44,31 +44,16 @@ class PythonQtSlotInfo; struct PythonQtMemberInfo { enum Type { - Invalid, Slot, EnumValue, EnumWrapper, Property, NotFound + Invalid, Slot, Signal, EnumValue, EnumWrapper, Property, NotFound }; PythonQtMemberInfo():_type(Invalid),_slot(NULL),_enumWrapper(NULL),_enumValue(0) { } - PythonQtMemberInfo(PythonQtSlotInfo* info) { - _type = Slot; - _slot = info; - _enumValue = NULL; - } + PythonQtMemberInfo(PythonQtSlotInfo* info); - PythonQtMemberInfo(const PythonQtObjectPtr& enumValue) { - _type = EnumValue; - _slot = NULL; - _enumValue = enumValue; - _enumWrapper = NULL; - } + PythonQtMemberInfo(const PythonQtObjectPtr& enumValue); - PythonQtMemberInfo(const QMetaProperty& prop) { - _type = Property; - _slot = NULL; - _enumValue = NULL; - _property = prop; - _enumWrapper = NULL; - } + PythonQtMemberInfo(const QMetaProperty& prop); Type _type; @@ -159,8 +144,8 @@ public: //! get list of all properties (on QObjects only, otherwise the list is empty) QStringList propertyList(); - //! get list of all members - QStringList memberList(bool metaOnly = false); + //! get list of all members (excluding properties, which can be listed with propertyList()) + QStringList memberList(); //! get the meta type id of this class (only valid for isCPPWrapper() == true) int metaTypeId() { return _metaTypeId; } diff --git a/src/PythonQtClassWrapper.cpp b/src/PythonQtClassWrapper.cpp index 561074f..17ffbcf 100644 --- a/src/PythonQtClassWrapper.cpp +++ b/src/PythonQtClassWrapper.cpp @@ -44,6 +44,7 @@ #include "PythonQt.h" #include "PythonQtSlot.h" +#include "PythonQtSignal.h" #include "PythonQtClassInfo.h" #include "PythonQtConversion.h" #include "PythonQtInstanceWrapper.h" @@ -291,40 +292,8 @@ PyObject *PythonQtClassWrapper_inherits(PythonQtClassWrapper *type, PyObject *ar return PythonQtConv::GetPyBool(wrapper->classInfo()->inherits(name)); } -PyObject *PythonQtClassWrapper__init__(PythonQtClassWrapper *type, PyObject *args) -{ - Py_ssize_t argc = PyTuple_Size(args); - if (argc>0) { - // we need to call __init__ of the instance - PyObject* self = PyTuple_GET_ITEM(args, 0); - if (PyObject_TypeCheck(self, (PyTypeObject*)type->classInfo()->pythonQtClassWrapper())) { - PyObject* newargs = PyTuple_New(argc-1); - for (int i = 0;itp_dict; + PyObject* objectDict = ((PyTypeObject *)wrapper)->tp_dict; if (!wrapper->classInfo()) { - Py_INCREF(dict); - return dict; + Py_INCREF(objectDict); + return objectDict; } - dict = PyDict_Copy(dict); - - QStringList l = wrapper->classInfo()->memberList(false); + PyObject* dict = PyDict_New(); + + QStringList l = wrapper->classInfo()->memberList(); foreach (QString name, l) { PyObject* o = PyObject_GetAttrString(obj, name.toLatin1().data()); if (o) { @@ -369,21 +338,33 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj, PyObject *name) Py_DECREF(o); } else { // it must have been a property or child, which we do not know as a class object... + PyErr_Clear(); } } if (wrapper->classInfo()->constructors()) { - PyObject* func = PyCFunction_New(&PythonQtClassWrapper_methods[0], obj); + PyObject* initName = PyString_FromString("__init__"); + PyObject* func = PyType_Type.tp_getattro(obj, initName); + Py_DECREF(initName); PyDict_SetItemString(dict, "__init__", func); Py_DECREF(func); } - for (int i = 1; PythonQtClassWrapper_methods[i].ml_name != NULL; i++) { + for (int i = 0; PythonQtClassWrapper_methods[i].ml_name != NULL; i++) { PyObject* func = PyCFunction_New(&PythonQtClassWrapper_methods[i], obj); PyDict_SetItemString(dict, PythonQtClassWrapper_methods[i].ml_name, func); Py_DECREF(func); } + + PyDict_Update(dict, objectDict); return dict; } + // look in Python to support derived Python classes + PyObject* superAttr = PyType_Type.tp_getattro(obj, name); + if (superAttr) { + return superAttr; + } + PyErr_Clear(); + if (wrapper->classInfo()) { PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName); if (member._type == PythonQtMemberInfo::EnumValue) { @@ -397,21 +378,17 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj, PyObject *name) } 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); + } else if (member._type == PythonQtMemberInfo::Signal) { + // we return all signals, even the instance signals, since they are callable as unbound signals with self argument + return PythonQtSignalFunction_New(member._slot, obj, NULL); } } - // look for the interal methods (className(), help()) + // look for the internal methods (className(), help()) PyObject* internalMethod = Py_FindMethod( PythonQtClassWrapper_methods, obj, (char*)attributeName); if (internalMethod) { return internalMethod; } - PyErr_Clear(); - - // look in super - PyObject* superAttr = PyType_Type.tp_getattro(obj, name); - if (superAttr) { - return superAttr; - } QString error = QString(wrapper->classInfo()->className()) + " has no attribute named '" + QString(attributeName) + "'"; PyErr_SetString(PyExc_AttributeError, error.toLatin1().data()); diff --git a/src/PythonQtImportFileInterface.h b/src/PythonQtImportFileInterface.h index b3871e9..d012b91 100644 --- a/src/PythonQtImportFileInterface.h +++ b/src/PythonQtImportFileInterface.h @@ -70,6 +70,11 @@ public: //! indicates that *.py files which are newer than their corresponding *.pyc files //! are ignored virtual bool ignoreUpdatedPythonSourceFiles() { return false; } + + //! called by PythonQt after successful import to allow + //! recording of imports + virtual void importedModule(const QString& /*module*/) {}; + }; #endif diff --git a/src/PythonQtImporter.cpp b/src/PythonQtImporter.cpp index f9e06a5..733d917 100644 --- a/src/PythonQtImporter.cpp +++ b/src/PythonQtImporter.cpp @@ -255,7 +255,13 @@ PythonQtImporter_load_module(PyObject *obj, PyObject *args) return NULL; } } + mod = PyImport_ExecCodeModuleEx(fullname, code, fullPath.toLatin1().data()); + + if (PythonQt::importInterface()) { + PythonQt::importInterface()->importedModule(fullname); + } + Py_DECREF(code); if (Py_VerboseFlag) { PySys_WriteStderr("import %s # loaded from %s\n", diff --git a/src/PythonQtInstanceWrapper.cpp b/src/PythonQtInstanceWrapper.cpp index b9eb052..68eda99 100644 --- a/src/PythonQtInstanceWrapper.cpp +++ b/src/PythonQtInstanceWrapper.cpp @@ -43,6 +43,7 @@ #include #include "PythonQt.h" #include "PythonQtSlot.h" +#include "PythonQtSignal.h" #include "PythonQtClassInfo.h" #include "PythonQtConversion.h" #include "PythonQtClassWrapper.h" @@ -413,6 +414,9 @@ static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name) case PythonQtMemberInfo::Slot: return PythonQtSlotFunction_New(member._slot, obj, NULL); break; + case PythonQtMemberInfo::Signal: + return PythonQtSignalFunction_New(member._slot, obj, NULL); + break; case PythonQtMemberInfo::EnumValue: { PyObject* enumValue = member._enumValue; @@ -529,6 +533,8 @@ static int PythonQtInstanceWrapper_setattro(PyObject *obj,PyObject *name,PyObjec } } else if (member._type == PythonQtMemberInfo::Slot) { error = QString("Slot '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object"; + } else if (member._type == PythonQtMemberInfo::Signal) { + error = QString("Signal '") + 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) { @@ -590,14 +596,16 @@ static QString getStringFromObject(PythonQtInstanceWrapper* wrapper) { return result; } } - // next, try to call py_toString - PythonQtMemberInfo info = wrapper->classInfo()->member("py_toString"); - if (info._type == PythonQtMemberInfo::Slot) { - PyObject* resultObj = PythonQtSlotFunction_CallImpl(wrapper->classInfo(), wrapper->_obj, info._slot, NULL, NULL, wrapper->_wrappedPtr); - if (resultObj) { - // TODO this is one conversion too much, would be nicer to call the slot directly... - result = PythonQtConv::PyObjGetString(resultObj); - Py_DECREF(resultObj); + if (wrapper->_wrappedPtr || wrapper->_obj) { + // next, try to call py_toString + PythonQtMemberInfo info = wrapper->classInfo()->member("py_toString"); + if (info._type == PythonQtMemberInfo::Slot) { + PyObject* resultObj = PythonQtSlotFunction_CallImpl(wrapper->classInfo(), wrapper->_obj, info._slot, NULL, NULL, wrapper->_wrappedPtr); + if (resultObj) { + // TODO this is one conversion too much, would be nicer to call the slot directly... + result = PythonQtConv::PyObjGetString(resultObj); + Py_DECREF(resultObj); + } } } return result; diff --git a/src/PythonQtMethodInfo.cpp b/src/PythonQtMethodInfo.cpp index dddcca9..9925aea 100644 --- a/src/PythonQtMethodInfo.cpp +++ b/src/PythonQtMethodInfo.cpp @@ -99,7 +99,9 @@ const PythonQtMethodInfo* PythonQtMethodInfo::getCachedMethodInfoFromArgumentLis if (i>1) { fullSig += ","; } - arguments << QByteArray(args[i]); + QByteArray arg(args[i]); + fullSig += arg; + arguments << arg; } fullSig += ")"; PythonQtMethodInfo* result = _cachedSignatures.value(fullSig); diff --git a/src/PythonQtSignal.cpp b/src/PythonQtSignal.cpp new file mode 100644 index 0000000..a752e62 --- /dev/null +++ b/src/PythonQtSignal.cpp @@ -0,0 +1,350 @@ +/* +* +* Copyright (C) 2010 MeVis Medical Solutions AG All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* Further, this software is distributed without any warranty that it is +* free of the rightful claim of any third person regarding infringement +* or the like. Any license provided herein, whether implied or +* otherwise, applies only to this software file. Patent licenses, if +* any, provided herein do not apply to combinations of this program with +* other software, or any other product whatsoever. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Contact information: MeVis Medical Solutions AG, Universitaetsallee 29, +* 28359 Bremen, Germany or: +* +* http://www.mevis.de +* +*/ + +//---------------------------------------------------------------------------------- +/*! +// \file PythonQtSignal.cpp +// \author Florian Link +// \author Last changed by $Author: florian $ +// \date 2012-02 +*/ +//---------------------------------------------------------------------------------- + +#include "PythonQt.h" +#include "PythonQtSignal.h" +#include "PythonQtInstanceWrapper.h" +#include "PythonQtClassInfo.h" +#include "PythonQtMisc.h" +#include "PythonQtConversion.h" +#include "PythonQtSlot.h" + +#include + +#include +#include + +#include + +//----------------------------------------------------------------------------------- + +static PythonQtSignalFunctionObject *PythonQtSignal_free_list = NULL; + +PyObject *PythonQtSignalFunction_Call(PyObject *func, PyObject *args, PyObject *kw) +{ + PythonQtSignalFunctionObject* f = (PythonQtSignalFunctionObject*)func; + return PythonQtMemberFunction_Call(f->m_ml, f->m_self, args, kw); +} + +PyObject * +PythonQtSignalFunction_New(PythonQtSlotInfo *ml, PyObject *self, PyObject *module) +{ + PythonQtSignalFunctionObject *op; + op = PythonQtSignal_free_list; + if (op != NULL) { + PythonQtSignal_free_list = (PythonQtSignalFunctionObject *)(op->m_self); + PyObject_INIT(op, &PythonQtSignalFunction_Type); + } + else { + op = PyObject_GC_New(PythonQtSignalFunctionObject, &PythonQtSignalFunction_Type); + if (op == NULL) + return NULL; + } + op->m_ml = ml; + Py_XINCREF(self); + op->m_self = self; + Py_XINCREF(module); + op->m_module = module; + PyObject_GC_Track(op); + return (PyObject *)op; +} + +/* Methods (the standard built-in methods, that is) */ + +static void +meth_dealloc(PythonQtSignalFunctionObject *m) +{ + PyObject_GC_UnTrack(m); + Py_XDECREF(m->m_self); + Py_XDECREF(m->m_module); + m->m_self = (PyObject *)PythonQtSignal_free_list; + PythonQtSignal_free_list = m; +} + +static PyObject * +meth_get__doc__(PythonQtSignalFunctionObject * /*m*/, void * /*closure*/) +{ + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +meth_get__name__(PythonQtSignalFunctionObject *m, void * /*closure*/) +{ + return PyString_FromString(m->m_ml->metaMethod()->signature()); +} + +static int +meth_traverse(PythonQtSignalFunctionObject *m, visitproc visit, void *arg) +{ + int err; + if (m->m_self != NULL) { + err = visit(m->m_self, arg); + if (err) + return err; + } + if (m->m_module != NULL) { + err = visit(m->m_module, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +meth_get__self__(PythonQtSignalFunctionObject *m, void * /*closure*/) +{ + PyObject *self; + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, + "method.__self__ not accessible in restricted mode"); + return NULL; + } + self = m->m_self; + if (self == NULL) + self = Py_None; + Py_INCREF(self); + return self; +} + +static PyGetSetDef meth_getsets [] = { + {const_cast("__doc__"), (getter)meth_get__doc__, NULL, NULL}, + {const_cast("__name__"), (getter)meth_get__name__, NULL, NULL}, + {const_cast("__self__"), (getter)meth_get__self__, NULL, NULL}, + {NULL, NULL, NULL,NULL}, +}; + +#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 6 +#define PY_WRITE_RESTRICTED WRITE_RESTRICTED +#endif + +#define OFF(x) offsetof(PythonQtSignalFunctionObject, x) + +static PyMemberDef meth_members[] = { + {const_cast("__module__"), T_OBJECT, OFF(m_module), PY_WRITE_RESTRICTED}, + {NULL} +}; + +static PyObject *PythonQtSignalFunction_parameterTypes(PythonQtSignalFunctionObject* type) +{ + return PythonQtMemberFunction_parameterTypes(type->m_ml); +} + +static PyObject *PythonQtSignalFunction_parameterNames(PythonQtSignalFunctionObject* type) +{ + return PythonQtMemberFunction_parameterNames(type->m_ml); +} + +static PyObject *PythonQtSignalFunction_typeName(PythonQtSignalFunctionObject* type) +{ + return PythonQtMemberFunction_typeName(type->m_ml); +} + +static PyObject *PythonQtSignalFunction_connect(PythonQtSignalFunctionObject* type, PyObject *args) +{ + if (PyObject_TypeCheck(type->m_self, &PythonQtInstanceWrapper_Type)) { + PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*) type->m_self; + if (self->_obj) { + Py_ssize_t argc = PyTuple_Size(args); + if (argc==1) { + // connect with Python callable + PyObject* callable = PyTuple_GET_ITEM(args, 0); + bool result = PythonQt::self()->addSignalHandler(self->_obj, QByteArray("2") + type->m_ml->metaMethod()->signature(), callable); + return PythonQtConv::GetPyBool(result); + } else { + PyErr_SetString(PyExc_ValueError, "Called connect with wrong number of arguments"); + } + } + } + return NULL; +} + +static PyObject *PythonQtSignalFunction_disconnect(PythonQtSignalFunctionObject* type, PyObject *args) +{ + if (PyObject_TypeCheck(type->m_self, &PythonQtInstanceWrapper_Type)) { + PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*) type->m_self; + if (self->_obj) { + Py_ssize_t argc = PyTuple_Size(args); + QByteArray signal = QByteArray("2") + type->m_ml->metaMethod()->signature(); + if (argc==1) { + // disconnect with Python callable + PyObject* callable = PyTuple_GET_ITEM(args, 0); + bool result = PythonQt::self()->removeSignalHandler(self->_obj, signal, callable); + return PythonQtConv::GetPyBool(result); + } else if (argc==0) { + bool result = PythonQt::self()->removeSignalHandler(self->_obj, signal, NULL); + result |= QObject::disconnect(self->_obj, signal, NULL, NULL); + return PythonQtConv::GetPyBool(result); + } else { + PyErr_SetString(PyExc_ValueError, "Called disconnect with wrong number of arguments"); + } + } + } + return NULL; +} + +static PyObject *PythonQtSignalFunction_emit(PythonQtSignalFunctionObject* func, PyObject *args) +{ + PythonQtSignalFunctionObject* f = (PythonQtSignalFunctionObject*)func; + return PythonQtMemberFunction_Call(f->m_ml, f->m_self, args, NULL); +} + +static PyMethodDef meth_methods[] = { + {"parameterTypes", (PyCFunction)PythonQtSignalFunction_parameterTypes, METH_NOARGS, + "Returns a tuple of tuples of the C++ parameter types for all overloads of the signal" + }, + {"parameterNames", (PyCFunction)PythonQtSignalFunction_parameterNames, METH_NOARGS, + "Returns a tuple of tuples of the C++ parameter type names (if available), for all overloads of the signal" + }, + {"typeName", (PyCFunction)PythonQtSignalFunction_typeName, METH_NOARGS, + "Returns a tuple of the C++ return value types of each signal overload" + }, + {"connect", (PyCFunction)PythonQtSignalFunction_connect, METH_VARARGS, + "Connects the signal to the Python callable" + }, + {"disconnect", (PyCFunction)PythonQtSignalFunction_disconnect, METH_VARARGS, + "Disconnects the signal from the given Python callable or disconnects all if no argument is passed." + }, + {"emit", (PyCFunction)PythonQtSignalFunction_emit, METH_VARARGS, + "Emits the signal with given arguments" + }, + {NULL, NULL, 0 , NULL} /* Sentinel */ +}; + +static PyObject * +meth_repr(PythonQtSignalFunctionObject *f) +{ + if (f->m_self->ob_type == &PythonQtClassWrapper_Type) { + PythonQtClassWrapper* self = (PythonQtClassWrapper*) f->m_self; + return PyString_FromFormat("", + f->m_ml->slotName().data(), + self->classInfo()->className()); + } else { + return PyString_FromFormat("", + f->m_ml->slotName().data(), + f->m_self->ob_type->tp_name, + f->m_self); + } +} + +static int +meth_compare(PythonQtSignalFunctionObject *a, PythonQtSignalFunctionObject *b) +{ + if (a->m_self != b->m_self) + return (a->m_self < b->m_self) ? -1 : 1; + if (a->m_ml == b->m_ml) + return 0; + if (strcmp(a->m_ml->metaMethod()->signature(), b->m_ml->metaMethod()->signature()) < 0) + return -1; + else + return 1; +} + +static long +meth_hash(PythonQtSignalFunctionObject *a) +{ + long x,y; + if (a->m_self == NULL) + x = 0; + else { + x = PyObject_Hash(a->m_self); + if (x == -1) + return -1; + } + y = _Py_HashPointer((void*)(a->m_ml)); + if (y == -1) + return -1; + x ^= y; + if (x == -1) + x = -2; + return x; +} + + +PyTypeObject PythonQtSignalFunction_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "builtin_qt_signal", + sizeof(PythonQtSignalFunctionObject), + 0, + (destructor)meth_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)meth_compare, /* tp_compare */ + (reprfunc)meth_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + // TODO: implement tp_as_mapping to support overload resolution on the signal + 0, /* tp_as_mapping */ + (hashfunc)meth_hash, /* tp_hash */ + PythonQtSignalFunction_Call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)meth_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + meth_methods, /* tp_methods */ + meth_members, /* tp_members */ + meth_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ +}; + +/* Clear out the free list */ + +void +PythonQtSignalFunction_Fini(void) +{ + while (PythonQtSignal_free_list) { + PythonQtSignalFunctionObject *v = PythonQtSignal_free_list; + PythonQtSignal_free_list = (PythonQtSignalFunctionObject *)(v->m_self); + PyObject_GC_Del(v); + } +} + diff --git a/src/PythonQtSignal.h b/src/PythonQtSignal.h new file mode 100644 index 0000000..7c3e592 --- /dev/null +++ b/src/PythonQtSignal.h @@ -0,0 +1,68 @@ +#ifndef _PythonQtSignal_H +#define _PythonQtSignal_H + +/* + * + * Copyright (C) 2010 MeVis Medical Solutions AG All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact information: MeVis Medical Solutions AG, Universitaetsallee 29, + * 28359 Bremen, Germany or: + * + * http://www.mevis.de + * + */ + +//---------------------------------------------------------------------------------- +/*! +// \file PythonQtSignal.h +// \author Florian Link +// \author Last changed by $Author: florian $ +// \date 2006-05 +*/ +//---------------------------------------------------------------------------------- + +#include "PythonQtPythonInclude.h" + +#include "PythonQtSystem.h" +#include "structmember.h" + +class PythonQtSlotInfo; + +extern PYTHONQT_EXPORT PyTypeObject PythonQtSignalFunction_Type; + +#define PythonQtSignalFunction_Check(op) ((op)->ob_type == &PythonQtSignalFunction_Type) + +PyObject* PythonQtSignalFunction_New(PythonQtSlotInfo *, PyObject *, + PyObject *); + +//! defines a python object that stores a Qt signal info +typedef struct { + PyObject_HEAD + PythonQtSlotInfo *m_ml; /* Description of the C function to call */ + PyObject *m_self; /* Passed as 'self' arg to the C func, can be NULL */ + PyObject *m_module; /* The __module__ attribute, can be anything */ +} PythonQtSignalFunctionObject; + + +#endif diff --git a/src/PythonQtSignalReceiver.cpp b/src/PythonQtSignalReceiver.cpp index b10086a..1b79f9a 100644 --- a/src/PythonQtSignalReceiver.cpp +++ b/src/PythonQtSignalReceiver.cpp @@ -66,7 +66,7 @@ PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInf PyObject* o = callable; PyFunctionObject* func = (PyFunctionObject*)o; PyCodeObject* code = (PyCodeObject*)func->func_code; - if (!(code->co_flags & 0x04)) { + if (!(code->co_flags & CO_VARARGS)) { numPythonArgs = code->co_argcount; } else { // variable numbers of arguments allowed @@ -77,7 +77,7 @@ PyObject* PythonQtSignalTarget::call(PyObject* callable, const PythonQtMethodInf if (PyFunction_Check(method->im_func)) { PyFunctionObject* func = (PyFunctionObject*)method->im_func; PyCodeObject* code = (PyCodeObject*)func->func_code; - if (!(code->co_flags & 0x04)) { + if (!(code->co_flags & CO_VARARGS)) { numPythonArgs = code->co_argcount - 1; // we subtract one because the first is "self" } else { // variable numbers of arguments allowed @@ -187,11 +187,20 @@ bool PythonQtSignalReceiver::removeSignalHandler(const char* signal, PyObject* c int sigId = getSignalIndex(signal); if (sigId>=0) { QMutableListIterator i(_targets); - while (i.hasNext()) { - if (i.next().isSame(sigId, callable)) { - i.remove(); - found = true; - break; + if (callable) { + while (i.hasNext()) { + if (i.next().isSame(sigId, callable)) { + i.remove(); + found = true; + break; + } + } + } else { + while (i.hasNext()) { + if (i.next().signalId() == sigId) { + i.remove(); + found = true; + } } } } diff --git a/src/PythonQtSignalReceiver.h b/src/PythonQtSignalReceiver.h index 56d915a..90ef3fc 100644 --- a/src/PythonQtSignalReceiver.h +++ b/src/PythonQtSignalReceiver.h @@ -118,8 +118,8 @@ public: //! add a signal handler bool addSignalHandler(const char* signal, PyObject* callable); - //! remove a signal handler - bool removeSignalHandler(const char* signal, PyObject* callable); + //! remove a signal handler for given callable (or all callables on that signal if callable is NULL) + bool removeSignalHandler(const char* signal, PyObject* callable = NULL); //! remove all signal handlers void removeSignalHandlers(); @@ -140,3 +140,4 @@ private: #endif + diff --git a/src/PythonQtSlot.cpp b/src/PythonQtSlot.cpp index 401b54e..a1740c2 100644 --- a/src/PythonQtSlot.cpp +++ b/src/PythonQtSlot.cpp @@ -50,6 +50,8 @@ #include #include +#include + #define PYTHONQT_MAX_ARGS 32 @@ -160,28 +162,34 @@ bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObj // invoke the slot via metacall bool hadException = false; - try { - (info->decorator()?info->decorator():objectToCall)->qt_metacall(QMetaObject::InvokeMetaMethod, info->slotIndex(), argList); - } catch (std::bad_alloc & e) { - hadException = true; - QByteArray what("std::bad_alloc: "); - what += e.what(); - PyErr_SetString(PyExc_MemoryError, what.constData()); - } catch (std::runtime_error & e) { - hadException = true; - QByteArray what("std::runtime_error: "); - what += e.what(); - PyErr_SetString(PyExc_RuntimeError, what.constData()); - } catch (std::logic_error & e) { + QObject* obj = info->decorator()?info->decorator():objectToCall; + if (!obj) { hadException = true; - QByteArray what("std::logic_error: "); - what += e.what(); - PyErr_SetString(PyExc_RuntimeError, what.constData()); - } catch (std::exception& e) { - hadException = true; - QByteArray what("std::exception: "); - what += e.what(); - PyErr_SetString(PyExc_StandardError, what.constData()); + PyErr_SetString(PyExc_RuntimeError, "Trying to call a slot on a deleted QObject!"); + } else { + try { + obj->qt_metacall(QMetaObject::InvokeMetaMethod, info->slotIndex(), argList); + } catch (std::bad_alloc & e) { + hadException = true; + QByteArray what("std::bad_alloc: "); + what += e.what(); + PyErr_SetString(PyExc_MemoryError, what.constData()); + } catch (std::runtime_error & e) { + hadException = true; + QByteArray what("std::runtime_error: "); + what += e.what(); + PyErr_SetString(PyExc_RuntimeError, what.constData()); + } catch (std::logic_error & e) { + hadException = true; + QByteArray what("std::logic_error: "); + what += e.what(); + PyErr_SetString(PyExc_RuntimeError, what.constData()); + } catch (std::exception& e) { + hadException = true; + QByteArray what("std::exception: "); + what += e.what(); + PyErr_SetString(PyExc_StandardError, what.constData()); + } } if (profilingCB) { @@ -227,18 +235,22 @@ static PythonQtSlotFunctionObject *pythonqtslot_free_list = NULL; PyObject *PythonQtSlotFunction_Call(PyObject *func, PyObject *args, PyObject *kw) { PythonQtSlotFunctionObject* f = (PythonQtSlotFunctionObject*)func; - PythonQtSlotInfo* info = f->m_ml; - if (PyObject_TypeCheck(f->m_self, &PythonQtInstanceWrapper_Type)) { - PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*) f->m_self; + return PythonQtMemberFunction_Call(f->m_ml, f->m_self, args, kw); +} + +PyObject *PythonQtMemberFunction_Call(PythonQtSlotInfo* info, PyObject* m_self, PyObject *args, PyObject *kw) +{ + if (PyObject_TypeCheck(m_self, &PythonQtInstanceWrapper_Type)) { + PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*) m_self; if (!info->isClassDecorator() && (self->_obj==NULL && self->_wrappedPtr==NULL)) { - QString error = QString("Trying to call '") + f->m_ml->slotName() + "' on a destroyed " + self->classInfo()->className() + " object"; + QString error = QString("Trying to call '") + info->slotName() + "' on a destroyed " + self->classInfo()->className() + " object"; PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); return NULL; } else { return PythonQtSlotFunction_CallImpl(self->classInfo(), self->_obj, info, args, kw, self->_wrappedPtr); } - } else if (f->m_self->ob_type == &PythonQtClassWrapper_Type) { - PythonQtClassWrapper* type = (PythonQtClassWrapper*) f->m_self; + } else if (m_self->ob_type == &PythonQtClassWrapper_Type) { + PythonQtClassWrapper* type = (PythonQtClassWrapper*) m_self; if (info->isClassDecorator()) { return PythonQtSlotFunction_CallImpl(type->classInfo(), NULL, info, args, kw); } else { @@ -250,7 +262,7 @@ PyObject *PythonQtSlotFunction_Call(PyObject *func, PyObject *args, PyObject *kw && ((PythonQtInstanceWrapper*)firstArg)->classInfo()->inherits(type->classInfo())) { PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*)firstArg; if (!info->isClassDecorator() && (self->_obj==NULL && self->_wrappedPtr==NULL)) { - QString error = QString("Trying to call '") + f->m_ml->slotName() + "' on a destroyed " + self->classInfo()->className() + " object"; + QString error = QString("Trying to call '") + info->slotName() + "' on a destroyed " + self->classInfo()->className() + " object"; PyErr_SetString(PyExc_ValueError, error.toLatin1().data()); return NULL; } @@ -456,6 +468,96 @@ static PyMemberDef meth_members[] = { {NULL} }; +static PyObject *PythonQtSlotFunction_parameterTypes(PythonQtSlotFunctionObject* type) +{ + return PythonQtMemberFunction_parameterTypes(type->m_ml); +} + +static PyObject *PythonQtSlotFunction_parameterNames(PythonQtSlotFunctionObject* type) +{ + return PythonQtMemberFunction_parameterNames(type->m_ml); +} + +static PyObject *PythonQtSlotFunction_typeName(PythonQtSlotFunctionObject* type) +{ + return PythonQtMemberFunction_typeName(type->m_ml); +} + +PyObject *PythonQtMemberFunction_parameterTypes(PythonQtSlotInfo* theInfo) +{ + PythonQtSlotInfo* info = theInfo; + int count = 0; + while (info) { + info = info->nextInfo(); + count++; + } + info = theInfo; + PyObject* result = PyTuple_New(count); + for (int j = 0;j types = info->metaMethod()->parameterTypes(); + PyObject* tuple = PyTuple_New(types.count()); + for (int i = 0; inextInfo(); + PyTuple_SET_ITEM(result, j, tuple); + } + return result; +} + +PyObject *PythonQtMemberFunction_parameterNames(PythonQtSlotInfo* theInfo) +{ + PythonQtSlotInfo* info = theInfo; + int count = 0; + while (info) { + info = info->nextInfo(); + count++; + } + info = theInfo; + PyObject* result = PyTuple_New(count); + for (int j = 0;j names = info->metaMethod()->parameterNames(); + PyObject* tuple = PyTuple_New(names.count()); + for (int i = 0; inextInfo(); + PyTuple_SET_ITEM(result, j, tuple); + } + return result; +} + +PyObject *PythonQtMemberFunction_typeName(PythonQtSlotInfo* theInfo) +{ + PythonQtSlotInfo* info = theInfo; + int count = 0; + while (info) { + info = info->nextInfo(); + count++; + } + info = theInfo; + PyObject* result = PyTuple_New(count); + for (int j = 0;jmetaMethod()->typeName(); + PyTuple_SET_ITEM(result, j, PyString_FromString(name.constData())); + info = info->nextInfo(); + } + return result; +} + +static PyMethodDef meth_methods[] = { + {"parameterTypes", (PyCFunction)PythonQtSlotFunction_parameterTypes, METH_NOARGS, + "Returns a tuple of tuples of the C++ parameter types for all overloads of the slot" + }, + {"parameterNames", (PyCFunction)PythonQtSlotFunction_parameterNames, METH_NOARGS, + "Returns a tuple of tuples of the C++ parameter type names (if available), for all overloads of the slot" + }, + {"typeName", (PyCFunction)PythonQtSlotFunction_typeName, METH_NOARGS, + "Returns a tuple of the C++ return value types of each slot overload" + }, + {NULL, NULL, 0 , NULL} /* Sentinel */ +}; + static PyObject * meth_repr(PythonQtSlotFunctionObject *f) { @@ -535,7 +637,7 @@ PyTypeObject PythonQtSlotFunction_Type = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + meth_methods, /* tp_methods */ meth_members, /* tp_members */ meth_getsets, /* tp_getset */ 0, /* tp_base */ diff --git a/src/PythonQtSlot.h b/src/PythonQtSlot.h index d81ce70..4c8b617 100644 --- a/src/PythonQtSlot.h +++ b/src/PythonQtSlot.h @@ -65,10 +65,14 @@ PyObject* PythonQtSlotFunction_Call(PyObject *, PyObject *, PyObject *); PyObject *PythonQtSlotFunction_CallImpl(PythonQtClassInfo* classInfo, QObject* objectToCall, PythonQtSlotInfo* info, PyObject *args, PyObject *kw, void* firstArg=NULL, void** directReturnValuePointer=NULL); - PyObject* PythonQtSlotFunction_New(PythonQtSlotInfo *, PyObject *, PyObject *); +PyObject *PythonQtMemberFunction_Call(PythonQtSlotInfo* info, PyObject* m_self, PyObject *args, PyObject *kw); +PyObject *PythonQtMemberFunction_parameterTypes(PythonQtSlotInfo* theInfo); +PyObject *PythonQtMemberFunction_parameterNames(PythonQtSlotInfo* theInfo); +PyObject *PythonQtMemberFunction_typeName(PythonQtSlotInfo* theInfo); + //! defines a python object that stores a Qt slot info typedef struct { PyObject_HEAD diff --git a/src/PythonQtStdDecorators.cpp b/src/PythonQtStdDecorators.cpp index 02f3044..7ddca2a 100644 --- a/src/PythonQtStdDecorators.cpp +++ b/src/PythonQtStdDecorators.cpp @@ -102,6 +102,9 @@ bool PythonQtStdDecorators::disconnect(QObject* sender, const QByteArray& signal } if (sender) { result = PythonQt::self()->removeSignalHandler(sender, signalTmp, callable); + if (callable == NULL) { + result |= QObject::disconnect(sender, signalTmp, NULL, NULL); + } if (!result) { if (sender->metaObject()->indexOfSignal(QMetaObject::normalizedSignature(signalTmp.constData()+1)) == -1) { qWarning("PythonQt: QObject::disconnect() signal '%s' does not exist on %s", signal.constData(), sender->metaObject()->className()); diff --git a/src/PythonQtStdDecorators.h b/src/PythonQtStdDecorators.h index 2d90af2..673274b 100644 --- a/src/PythonQtStdDecorators.h +++ b/src/PythonQtStdDecorators.h @@ -61,7 +61,7 @@ class PYTHONQT_EXPORT PythonQtStdDecorators : public QObject public slots: bool connect(QObject* sender, const QByteArray& signal, PyObject* callable); bool connect(QObject* sender, const QByteArray& signal, QObject* receiver, const QByteArray& slot); - bool disconnect(QObject* sender, const QByteArray& signal, PyObject* callable); + bool disconnect(QObject* sender, const QByteArray& signal, PyObject* callable = NULL); bool disconnect(QObject* sender, const QByteArray& signal, QObject* receiver, const QByteArray& slot); QObject* parent(QObject* o); diff --git a/src/src.pri b/src/src.pri index dbf0a18..a318221 100644 --- a/src/src.pri +++ b/src/src.pri @@ -6,6 +6,7 @@ HEADERS += \ $$PWD/PythonQtClassInfo.h \ $$PWD/PythonQtImporter.h \ $$PWD/PythonQtObjectPtr.h \ + $$PWD/PythonQtSignal.h \ $$PWD/PythonQtSlot.h \ $$PWD/PythonQtStdIn.h \ $$PWD/PythonQtStdOut.h \ @@ -31,6 +32,7 @@ SOURCES += \ $$PWD/PythonQtObjectPtr.cpp \ $$PWD/PythonQtStdIn.cpp \ $$PWD/PythonQtStdOut.cpp \ + $$PWD/PythonQtSignal.cpp \ $$PWD/PythonQtSlot.cpp \ $$PWD/PythonQtMisc.cpp \ $$PWD/PythonQtMethodInfo.cpp \