diff options
author | Hugo Lima <hugo.lima@openbossa.org> | 2009-12-29 17:04:56 -0200 |
---|---|---|
committer | Hugo Lima <hugo.lima@openbossa.org> | 2010-01-04 15:48:36 -0200 |
commit | 85048bbd6c5d4231f71852cab18fd581c5068141 (patch) | |
tree | f1a1b38b04e748c0c50825d51d38c7dafe7ee60e /libpyside/signalmanager.cpp | |
parent | e8e1ecc260028d045efcbc3c71d7a50009950f5b (diff) |
Fixes problems calling dynamic python slots.
Reviewed by Marcelo Lira <marcelo.lira@openbossa.org>
Diffstat (limited to 'libpyside/signalmanager.cpp')
-rw-r--r-- | libpyside/signalmanager.cpp | 144 |
1 files changed, 99 insertions, 45 deletions
diff --git a/libpyside/signalmanager.cpp b/libpyside/signalmanager.cpp index a222e0371..c8b55e77f 100644 --- a/libpyside/signalmanager.cpp +++ b/libpyside/signalmanager.cpp @@ -65,44 +65,75 @@ bool PySide::checkSignal(const char* signal) return true; } -QString PySide::getCallbackSignature(const char* signal, PyObject* callback) +static QString codeCallbackName(PyObject* callback, const QString& funcName) { - PyObject* function; - int useSelf = PyMethod_Check(callback); - if (useSelf) { - function = PyMethod_GET_FUNCTION(callback); - } else { - function = callback; + return funcName+QString::number(quint64(callback), 16); +} + +QString PySide::getCallbackSignature(const char* signal, PyObject* callback, bool encodeName) +{ + QString functionName; + int numArgs = -1; + bool useSelf = false; + + bool isMethod = PyMethod_Check(callback); + bool isFunction = PyFunction_Check(callback); + + if (isMethod || isFunction) { + PyObject* function = isMethod ? PyMethod_GET_FUNCTION(callback) : callback; + PyCodeObject* objCode = reinterpret_cast<PyCodeObject*>(PyFunction_GET_CODE(function)); + functionName = PyString_AS_STRING(objCode->co_name); + useSelf = isMethod; + numArgs = objCode->co_flags & CO_VARARGS ? -1 : objCode->co_argcount; + } else if (PyCFunction_Check(callback)) { + functionName = ((PyCFunctionObject*)callback)->m_ml->ml_name; + useSelf = ((PyCFunctionObject*)callback)->m_self; + int flags = ((PyCFunctionObject*)callback)->m_ml->ml_flags; + if (flags & METH_O) + numArgs = 1; + else if (flags & METH_VARARGS) + numArgs = -1; + else if (flags & METH_NOARGS) + numArgs = 0; } + Q_ASSERT(!functionName.isEmpty()); + + QString signature; + if (encodeName) + signature = codeCallbackName(callback, functionName); + else + signature = functionName; - PyCodeObject* objCode = reinterpret_cast<PyCodeObject*>(PyFunction_GET_CODE(function)); - QString signature(PyString_AS_STRING(objCode->co_name)); - signature.append(QString::number(quint64(callback), 16)); - signature.append('('); - int numArgs = objCode->co_flags & CO_VARARGS ? -1 : objCode->co_argcount; - - QStringList args = getArgsFromSignature(signal); - if (numArgs == -1) - numArgs = std::numeric_limits<int>::max(); - while (args.count() > numArgs - useSelf) { - args.removeLast(); + bool isShortCircuit; + QStringList args = getArgsFromSignature(signal, &isShortCircuit); + + if (!isShortCircuit) { + signature.append('('); + if (numArgs == -1) + numArgs = std::numeric_limits<int>::max(); + while (args.count() > numArgs - useSelf) { + args.removeLast(); + } + signature.append(args.join(",")); + signature.append(')'); } - signature.append(args.join(",")); - signature.append(')'); return signature; } -QStringList PySide::getArgsFromSignature(const char* signature) +QStringList PySide::getArgsFromSignature(const char* signature, bool* isShortCircuit) { QString qsignature(signature); QStringList result; QRegExp splitRegex("\\s*,\\s*"); + if (isShortCircuit) + *isShortCircuit = !qsignature.contains('('); if (qsignature.contains("()") || qsignature.contains("(void)")) { return result; } else if (qsignature.contains('(')) { + static QRegExp regex(".+\\((.*)\\)"); //get args types - QString types = qsignature.replace(QRegExp(".+\\((.*)\\)"), "\\1"); + QString types = qsignature.replace(regex, "\\1"); result = types.split(splitRegex); } return result; @@ -138,31 +169,52 @@ void SignalManager::addGlobalSlot(const char* slot, PyObject* callback) m_d->m_globalReceiver.addSlot(slot, callback); } +static bool emitShortCircuitSignal(QObject* source, int signalIndex, PyObject* args) +{ + void* signalArgs[2] = {0, args}; + QMetaObject::activate(source, signalIndex, signalArgs); + return true; +} + +static bool emitNormalSignal(QObject* source, int signalIndex, const char* signal, PyObject* args, const QStringList& argTypes) +{ + int argsGiven = PySequence_Size(args); + if (argsGiven > argTypes.count()) { + QString msg = QString("%1 only accepts %2 arguments, %3 given!").arg(signal).arg(argTypes.count()).arg(argsGiven); + PyErr_SetString(PyExc_TypeError, msg.toLocal8Bit().constData()); + return false; + } + + void* signalArgs[argsGiven+1]; + signalArgs[0] = 0; + + for (int i = 0; i < argsGiven; ++i) + signalArgs[i+1] = TypeResolver::get(argTypes[i])->toCpp(PySequence_GetItem(args, i)); + QMetaObject::activate(source, signalIndex, signalArgs); + // FIXME: This will cause troubles with non-direct connections. + for (int i = 0; i < argsGiven; ++i) + TypeResolver::get(argTypes[i])->deleteObject(signalArgs[i+1]); + return true; +} + bool SignalManager::emitSignal(QObject* source, const char* signal, PyObject* args) { if (!checkSignal(signal)) return false; signal++; - int argsGiven = PySequence_Size(args); int signalIndex = source->metaObject()->indexOfSignal(signal); if (signalIndex != -1) { - QStringList argTypes = getArgsFromSignature(signal); - if (argsGiven > argTypes.count()) { - PyErr_SetString(PyExc_TypeError, "Too many arguments for this signal."); - return false; - } - void* signalArgs[argsGiven+1]; - signalArgs[0] = 0; - for (int i = 0; i < argsGiven; ++i) - signalArgs[i+1] = TypeResolver::get(argTypes[i])->toCpp(PySequence_GetItem(args, i)); - QMetaObject::activate(source, signalIndex, signalArgs); - // FIXME: This will cause troubles with non-direct connections. - for (int i = 0; i < argsGiven; ++i) - TypeResolver::get(argTypes[i])->deleteObject(signalArgs[i+1]); - return true; + bool isShortCircuit; + QStringList argTypes = getArgsFromSignature(signal, &isShortCircuit); + + if (isShortCircuit) + return emitShortCircuitSignal(source, signalIndex, args); + else + return emitNormalSignal(source, signalIndex, signal, args, argTypes); } + qWarning() << "Error emitting signal: " << signal; return false; } @@ -183,22 +235,24 @@ int PySide::SignalManager::qt_metacall(QObject* object, QMetaObject::Call call, // call python slot QList<QByteArray> paramTypes = method.parameterTypes(); PyObject* self = Shiboken::BindingManager::instance().retrieveWrapper(object); - Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count()+1)); + Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count())); - PyTuple_SET_ITEM(preparedArgs.object(), 0, self); for (int i = 0, max = paramTypes.count(); i < max; ++i) { PyObject* arg = TypeResolver::get(paramTypes[i].constData())->toPython(args[i+1]); - PyTuple_SET_ITEM(preparedArgs.object(), i + 1, arg); + PyTuple_SET_ITEM(preparedArgs.object(), i, arg); } QString methodName = method.signature(); methodName = methodName.left(methodName.indexOf('(')); - Shiboken::AutoDecRef pyMethodName(PyString_FromString(qPrintable(methodName))); - Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(self, pyMethodName)); - Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, preparedArgs)); - if (!retval) - qWarning("Error calling slot"); + Shiboken::AutoDecRef pyMethod(PyObject_GetAttrString(self, qPrintable(methodName))); + if (pyMethod) { + Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, preparedArgs)); + if (!retval) + qWarning() << "Error calling slot" << methodName; + } else { + qWarning() << "Dynamic slot" << methodName << "not found!"; + } } return -1; } |