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 | |
parent | e8e1ecc260028d045efcbc3c71d7a50009950f5b (diff) |
Fixes problems calling dynamic python slots.
Reviewed by Marcelo Lira <marcelo.lira@openbossa.org>
-rw-r--r-- | PySide/QtCore/glue/qobject_connect.cpp | 6 | ||||
-rw-r--r-- | libpyside/globalreceiver.cpp | 39 | ||||
-rw-r--r-- | libpyside/globalreceiver.h | 2 | ||||
-rw-r--r-- | libpyside/signalmanager.cpp | 144 | ||||
-rw-r--r-- | libpyside/signalmanager.h | 4 |
5 files changed, 137 insertions, 58 deletions
diff --git a/PySide/QtCore/glue/qobject_connect.cpp b/PySide/QtCore/glue/qobject_connect.cpp index 5fe0ac427..846ab5012 100644 --- a/PySide/QtCore/glue/qobject_connect.cpp +++ b/PySide/QtCore/glue/qobject_connect.cpp @@ -54,13 +54,17 @@ static bool qobjectConnectCallback(QObject* source, const char* signal, PyObject self = PyMethod_GET_SELF(callback); if (SbkQObject_Check(self)) receiver = SbkQObject_cptr(self); + } else if (PyCFunction_Check(callback)) { + self = PyCFunction_GET_SELF(callback); + if (self && SbkQObject_Check(self)) + receiver = SbkQObject_cptr(self); } usingGlobalReceiver = !receiver; if (usingGlobalReceiver) receiver = signalManager.globalReceiver(); metaObject = receiver->metaObject(); - const QByteArray callbackSig = PySide::getCallbackSignature(signal, callback).toAscii(); + const QByteArray callbackSig = PySide::getCallbackSignature(signal, callback, usingGlobalReceiver).toAscii(); const char* slot = callbackSig.constData(); int slotIndex = metaObject->indexOfSlot(slot); if (slotIndex == -1) { diff --git a/libpyside/globalreceiver.cpp b/libpyside/globalreceiver.cpp index b058cf537..0d6ce7cf2 100644 --- a/libpyside/globalreceiver.cpp +++ b/libpyside/globalreceiver.cpp @@ -41,8 +41,7 @@ using namespace PySide; -GlobalReceiver::GlobalReceiver() - : m_metaObject("GlobalReceiver", &QObject::staticMetaObject) +GlobalReceiver::GlobalReceiver() : m_metaObject("GlobalReceiver", &QObject::staticMetaObject) { } @@ -64,6 +63,16 @@ void GlobalReceiver::addSlot(const char* slot, PyObject* callback) Py_INCREF(callback); m_slotReceivers[slotId] = callback; + bool isShortCircuit = true; + for (int i = 0; slot[i]; ++i) { + if (slot[i] == '(') { + isShortCircuit = false; + break; + } + } + if (isShortCircuit) + m_shortCircuitSlots << slotId; + Q_ASSERT(slotId >= QObject::staticMetaObject.methodCount()); } @@ -79,22 +88,32 @@ int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args) Q_ASSERT(id >= QObject::staticMetaObject.methodCount()); QMetaMethod slot = m_metaObject.method(id); Q_ASSERT(slot.methodType() == QMetaMethod::Slot); - QList<QByteArray> paramTypes = slot.parameterTypes(); PyObject* callback = m_slotReceivers.value(id); if (!callback) { - qWarning("Unknown global slot."); + qWarning() << "Unknown global slot, id:" << id; return -1; } - Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count())); - 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, arg); + int numArgs; + PyObject* retval = 0; + if (m_shortCircuitSlots.contains(id)) { + retval = PyObject_CallObject(callback, reinterpret_cast<PyObject*>(args[1])); + } else { + QList<QByteArray> paramTypes = slot.parameterTypes(); + numArgs = paramTypes.count(); + Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count())); + 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, arg); + } + + retval = PyObject_CallObject(callback, preparedArgs); } - Shiboken::AutoDecRef retval(PyObject_CallObject(callback, preparedArgs)); if (!retval) - qWarning("Error calling slot"); + qWarning() << "Error calling slot" << m_metaObject.method(id).signature(); + else + Py_DECREF(retval); return -1; } diff --git a/libpyside/globalreceiver.h b/libpyside/globalreceiver.h index 32fc71539..e1e230b7b 100644 --- a/libpyside/globalreceiver.h +++ b/libpyside/globalreceiver.h @@ -38,6 +38,7 @@ #include <Python.h> #include <QObject> #include <QHash> +#include <QSet> #include "dynamicqmetaobject.h" namespace PySide @@ -55,6 +56,7 @@ public: private: DynamicQMetaObject m_metaObject; QHash<int, PyObject* > m_slotReceivers; + QSet<int> m_shortCircuitSlots; }; } 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; } diff --git a/libpyside/signalmanager.h b/libpyside/signalmanager.h index 14ade8a6d..870aecdfe 100644 --- a/libpyside/signalmanager.h +++ b/libpyside/signalmanager.h @@ -47,8 +47,8 @@ namespace PySide PYSIDE_API bool isSignal(const char* signal); PYSIDE_API bool checkSignal(const char* signal); -PYSIDE_API QString getCallbackSignature(const char* signal, PyObject* callback); -QStringList getArgsFromSignature(const char* signature); +PYSIDE_API QString getCallbackSignature(const char* signal, PyObject* callback, bool encodeName); +QStringList getArgsFromSignature(const char* signature, bool* isShortCircuit = 0); class PYSIDE_API SignalManager { |